@_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,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import Iterable, List
|
|
5
|
+
|
|
6
|
+
from bs4 import BeautifulSoup
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
UNSUPPORTED_ISSUES_ORDER = [
|
|
10
|
+
"flex or grid layout (display: flex/grid)",
|
|
11
|
+
"positioning or floats (position/float)",
|
|
12
|
+
"pseudo-elements (::before/::after)",
|
|
13
|
+
"advanced selectors (#id, attribute selectors, sibling combinators, pseudo-classes)",
|
|
14
|
+
"unsupported visual effects (background-image, gradients, box-shadow, border-radius, transform)",
|
|
15
|
+
"unsupported units (em, rem, %, vh, vw)",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
_ISSUE_TO_PATTERNS = {
|
|
19
|
+
"flex or grid layout (display: flex/grid)": [
|
|
20
|
+
re.compile(r"display\s*:\s*(flex|grid)\b", re.IGNORECASE),
|
|
21
|
+
],
|
|
22
|
+
"positioning or floats (position/float)": [
|
|
23
|
+
re.compile(r"\bposition\s*:\s*(absolute|relative|fixed|sticky)\b", re.IGNORECASE),
|
|
24
|
+
re.compile(r"\bfloat\s*:\s*(left|right|inline-start|inline-end)\b", re.IGNORECASE),
|
|
25
|
+
],
|
|
26
|
+
"pseudo-elements (::before/::after)": [
|
|
27
|
+
re.compile(r"::before\b", re.IGNORECASE),
|
|
28
|
+
re.compile(r"::after\b", re.IGNORECASE),
|
|
29
|
+
],
|
|
30
|
+
"unsupported visual effects (background-image, gradients, box-shadow, border-radius, transform)": [
|
|
31
|
+
re.compile(r"\bbackground-image\s*:", re.IGNORECASE),
|
|
32
|
+
re.compile(r"\bbox-shadow\s*:", re.IGNORECASE),
|
|
33
|
+
re.compile(r"\bborder-radius\s*:", re.IGNORECASE),
|
|
34
|
+
re.compile(r"\btransform\s*:", re.IGNORECASE),
|
|
35
|
+
re.compile(r"gradient\s*\(", re.IGNORECASE),
|
|
36
|
+
],
|
|
37
|
+
"unsupported units (em, rem, %, vh, vw)": [
|
|
38
|
+
re.compile(r"(-?\d*\.?\d+)\s*(em|rem|%|vh|vw)\b", re.IGNORECASE),
|
|
39
|
+
],
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def find_unsupported_html(html_content: str) -> List[str]:
|
|
44
|
+
issues = set()
|
|
45
|
+
soup = BeautifulSoup(html_content, "html.parser")
|
|
46
|
+
|
|
47
|
+
for style_tag in soup.find_all("style"):
|
|
48
|
+
css_text = style_tag.get_text() or ""
|
|
49
|
+
_scan_css_text(css_text, issues)
|
|
50
|
+
_scan_css_selectors(css_text, issues)
|
|
51
|
+
|
|
52
|
+
for tag in soup.find_all(True):
|
|
53
|
+
inline_style = tag.get("style", "")
|
|
54
|
+
if inline_style:
|
|
55
|
+
_scan_css_text(inline_style, issues)
|
|
56
|
+
|
|
57
|
+
return [issue for issue in UNSUPPORTED_ISSUES_ORDER if issue in issues]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def build_unsupported_error(issues: Iterable[str]) -> str:
|
|
61
|
+
details = "\n".join(f"- {issue}" for issue in issues)
|
|
62
|
+
return f"Error: Unsupported HTML/CSS detected:\n{details}"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _scan_css_text(css_text: str, issues: set) -> None:
|
|
66
|
+
for issue, patterns in _ISSUE_TO_PATTERNS.items():
|
|
67
|
+
if issue in issues:
|
|
68
|
+
continue
|
|
69
|
+
if any(pattern.search(css_text) for pattern in patterns):
|
|
70
|
+
issues.add(issue)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _scan_css_selectors(css_text: str, issues: set) -> None:
|
|
74
|
+
if (
|
|
75
|
+
"advanced selectors (#id, attribute selectors, sibling combinators, pseudo-classes)"
|
|
76
|
+
in issues
|
|
77
|
+
):
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
for selectors in _iter_selectors(css_text):
|
|
81
|
+
for selector in selectors:
|
|
82
|
+
if _selector_has_unsupported(selector):
|
|
83
|
+
issues.add(
|
|
84
|
+
"advanced selectors (#id, attribute selectors, sibling combinators, pseudo-classes)"
|
|
85
|
+
)
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _iter_selectors(css_text: str) -> Iterable[List[str]]:
|
|
90
|
+
for match in re.finditer(r"([^{]+)\{[^}]*\}", css_text, re.DOTALL):
|
|
91
|
+
selector_text = match.group(1)
|
|
92
|
+
selectors = [s.strip() for s in selector_text.split(",") if s.strip()]
|
|
93
|
+
if selectors:
|
|
94
|
+
yield selectors
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _selector_has_unsupported(selector: str) -> bool:
|
|
98
|
+
if any(token in selector for token in ["#", "[", "]", "+", "~"]):
|
|
99
|
+
return True
|
|
100
|
+
if ":" in selector:
|
|
101
|
+
return True
|
|
102
|
+
return False
|
package/helpers.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from composio import Composio
|
|
4
|
+
from composio_openai_agents import OpenAIAgentsProvider
|
|
5
|
+
|
|
6
|
+
from dotenv import load_dotenv
|
|
7
|
+
load_dotenv()
|
|
8
|
+
|
|
9
|
+
_composio_clients: dict[str, Composio] = {}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_composio_user_id() -> str | None:
|
|
13
|
+
for key in ("COMPOSIO_USER_ID", "USER_ID"):
|
|
14
|
+
value = os.getenv(key)
|
|
15
|
+
if value:
|
|
16
|
+
return str(value)
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_composio_client() -> Composio | None:
|
|
21
|
+
api_key = os.getenv("COMPOSIO_API_KEY")
|
|
22
|
+
if not api_key:
|
|
23
|
+
return None
|
|
24
|
+
if api_key in _composio_clients:
|
|
25
|
+
return _composio_clients[api_key]
|
|
26
|
+
client = Composio(provider=OpenAIAgentsProvider())
|
|
27
|
+
_composio_clients[api_key] = client
|
|
28
|
+
return client
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def execute_composio_tool(tool_name: str, arguments: dict):
|
|
32
|
+
composio = get_composio_client()
|
|
33
|
+
user_id = get_composio_user_id()
|
|
34
|
+
if not composio:
|
|
35
|
+
return {"error": "COMPOSIO_API_KEY is not set."}
|
|
36
|
+
if not user_id:
|
|
37
|
+
return {"error": "COMPOSIO_USER_ID is not set."}
|
|
38
|
+
|
|
39
|
+
return composio.tools.execute(
|
|
40
|
+
tool_name,
|
|
41
|
+
user_id=user_id,
|
|
42
|
+
arguments=arguments,
|
|
43
|
+
dangerously_skip_version_check=True,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_composio_tools(**kwargs):
|
|
48
|
+
composio = get_composio_client()
|
|
49
|
+
user_id = get_composio_user_id()
|
|
50
|
+
if not composio:
|
|
51
|
+
return {"error": "COMPOSIO_API_KEY is not set."}
|
|
52
|
+
if not user_id:
|
|
53
|
+
return {"error": "COMPOSIO_USER_ID is not set."}
|
|
54
|
+
|
|
55
|
+
return composio.tools.get(user_id, **kwargs)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
user_id = get_composio_user_id()
|
|
59
|
+
composio = get_composio_client()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .image_generation_agent import create_image_generation_agent
|
|
Binary file
|
|
@@ -0,0 +1,31 @@
|
|
|
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_image_generation_agent() -> Agent:
|
|
9
|
+
return Agent(
|
|
10
|
+
name="Image Agent",
|
|
11
|
+
description="A specialized agent for image generation, editing, and composition.",
|
|
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 clean product hero image for my landing page.",
|
|
22
|
+
"Edit this uploaded photo to match a cinematic style.",
|
|
23
|
+
"Create two variants: one with Gemini and one with OpenAI image model.",
|
|
24
|
+
"Combine these images into a polished ad creative.",
|
|
25
|
+
],
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
from agency_swarm import Agency
|
|
31
|
+
Agency(create_image_generation_agent()).terminal_demo()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Role
|
|
2
|
+
|
|
3
|
+
You are an Image Generation Specialist focused on producing high-quality images and edits.
|
|
4
|
+
|
|
5
|
+
# Goals
|
|
6
|
+
|
|
7
|
+
- Generate images that match user intent with strong visual quality.
|
|
8
|
+
- Choose the best model for each request and explain that choice briefly.
|
|
9
|
+
- Use reference images when consistency or precise composition is required.
|
|
10
|
+
- Deliver outputs with clear delivery confirmations and visual previews.
|
|
11
|
+
|
|
12
|
+
# Process
|
|
13
|
+
|
|
14
|
+
## 1) Analyze Requirements
|
|
15
|
+
|
|
16
|
+
1. Identify whether the task is generation, editing, or composition.
|
|
17
|
+
2. Identify style, aspect ratio, realism level, and any mandatory elements.
|
|
18
|
+
3. Determine if reference images are required for consistency.
|
|
19
|
+
|
|
20
|
+
## 2) Select a Model
|
|
21
|
+
|
|
22
|
+
1. **Prefer `gemini-2.5-flash-image` by default** for most generation and editing tasks. It is the fastest high-quality option for iterative workflows and rapid variants.
|
|
23
|
+
2. **Use `gemini-3-pro-image-preview` for precision-first outputs** where detail quality matters more than speed:
|
|
24
|
+
- Text-heavy images (headlines, labels, typography)
|
|
25
|
+
- Complex product compositions with multiple visual constraints
|
|
26
|
+
- High-fidelity brand assets where prompt adherence is critical
|
|
27
|
+
- Large, highly detailed prompts with many constraints or style directives
|
|
28
|
+
- Complex and precise image editing tasks that require strict instruction following
|
|
29
|
+
3. **Use `gpt-image-1.5` when OpenAI is explicitly requested** or when the user asks for model comparison against Gemini outputs.
|
|
30
|
+
4. **Model-specific aspect-ratio awareness**:
|
|
31
|
+
- Gemini models support a broader AR set in these tools.
|
|
32
|
+
- `gpt-image-1.5` in this agent supports `1:1`, `2:3`, and `3:2`.
|
|
33
|
+
- If a requested AR is unsupported for the chosen model, switch to a compatible model and explain why.
|
|
34
|
+
5. Use a single model by default unless the user explicitly asks for multi-model output.
|
|
35
|
+
|
|
36
|
+
## 3) Execute with Tools
|
|
37
|
+
|
|
38
|
+
1. Use `GenerateImages` for text-to-image generation.
|
|
39
|
+
2. Use `EditImages` for reference-driven edits.
|
|
40
|
+
3. Use `CombineImages` when compositing multiple image references into one output. Should be used whenever user wants to put elements from one image into another image. For example, when user wants to put company logo from one image onto a product in another image.
|
|
41
|
+
4. Use `RemoveBackground` to strip the background from an image and produce a transparent PNG. Use this whenever the user asks to remove, cut out, or isolate the subject from its background.
|
|
42
|
+
5. If user uploaded files are provided, use those file references directly.
|
|
43
|
+
6. Include the file path in your response for every final user-facing output image/file.
|
|
44
|
+
|
|
45
|
+
## 4) Validate and Deliver
|
|
46
|
+
|
|
47
|
+
1. Perform a mandatory QC pass after every generation/edit:
|
|
48
|
+
- Compare result against user requirements for composition, scale, lighting, artifacts, and missing elements.
|
|
49
|
+
- Record issues explicitly as pass/fail checks.
|
|
50
|
+
- Analyze the photo as if user asks you "What's wrong with this image?"
|
|
51
|
+
2. If any issue is found, perform one automatic correction pass before final delivery:
|
|
52
|
+
- Use the same model for small fixes.
|
|
53
|
+
- Upgrade to `gemini-3-pro-image-preview` for precision/composition/complex-editing issues.
|
|
54
|
+
3. After auto-fix, run QC again and report final status.
|
|
55
|
+
4. If issues still remain, explicitly state that they remain and propose exactly one next change.
|
|
56
|
+
|
|
57
|
+
## 5) Final File Delivery
|
|
58
|
+
|
|
59
|
+
1. Include the file path in your response for every final user-facing output image/file.
|
|
60
|
+
2. Deliver only after QC is complete.
|
|
61
|
+
3. If multiple final variants are requested, list all paths together.
|
|
62
|
+
4. Do not include paths for intermediate test renders unless the user explicitly asks for them.
|
|
63
|
+
|
|
64
|
+
# Output Format
|
|
65
|
+
|
|
66
|
+
- Keep responses concise and action-oriented.
|
|
67
|
+
- Include:
|
|
68
|
+
- Model used (and upgrade reason if model changed)
|
|
69
|
+
- What was generated/edited
|
|
70
|
+
- Absolute output path(s) for each delivered file.
|
|
71
|
+
- A 2-5 bullet QC checklist with Pass/Fail status and what changed in auto-fix
|
|
72
|
+
- One optional improvement suggestion (only if fully passing result is not yet achieved)
|
|
73
|
+
|
|
74
|
+
# Additional Notes
|
|
75
|
+
|
|
76
|
+
- Do not sanitize or weaken user intent; pass requirements faithfully to generation tools.
|
|
77
|
+
- Avoid unnecessary parallel generation unless user asks for multiple variants or comparisons.
|
|
78
|
+
- Prefer continuity through references for character/product consistency across outputs.
|
|
79
|
+
- If quality is insufficient with `gemini-2.5-flash-image`, retry with `gemini-3-pro-image-preview` before proposing a major prompt rewrite.
|
|
80
|
+
- Never skip QC reporting, even if the result looks good at first glance.
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""Combine multiple image references into a single generated composition."""
|
|
2
|
+
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from openai import OpenAI
|
|
8
|
+
from PIL import Image
|
|
9
|
+
from pydantic import Field, field_validator, model_validator
|
|
10
|
+
|
|
11
|
+
from agency_swarm import BaseTool
|
|
12
|
+
|
|
13
|
+
from .utils.image_io import (
|
|
14
|
+
get_images_dir,
|
|
15
|
+
build_variant_output_name,
|
|
16
|
+
resolve_image_reference,
|
|
17
|
+
save_image,
|
|
18
|
+
image_to_base64_jpeg,
|
|
19
|
+
build_multimodal_outputs,
|
|
20
|
+
extract_gemini_image_and_usage,
|
|
21
|
+
extract_openai_images_and_usage,
|
|
22
|
+
run_parallel_variants_sync,
|
|
23
|
+
validate_aspect_ratio_for_model,
|
|
24
|
+
get_openai_size_for_aspect_ratio,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CombineImages(BaseTool):
|
|
29
|
+
"""
|
|
30
|
+
Combine multiple images into a single generated composition
|
|
31
|
+
using the selected model and instruction.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
product_name: str = Field(..., description="Product namespace for output files.")
|
|
35
|
+
image_refs: list[str] = Field(
|
|
36
|
+
...,
|
|
37
|
+
description="List of image references (URLs, absolute paths, or generated image names).",
|
|
38
|
+
)
|
|
39
|
+
text_instruction: str = Field(..., description="Instruction for how images should be combined.")
|
|
40
|
+
output_file_name: str = Field(
|
|
41
|
+
...,
|
|
42
|
+
description=(
|
|
43
|
+
"Output image name (without extension) or output path. "
|
|
44
|
+
"If a path is provided, the image is saved at that path."
|
|
45
|
+
),
|
|
46
|
+
)
|
|
47
|
+
model: Literal["gemini-2.5-flash-image", "gemini-3-pro-image-preview", "gpt-image-1.5"] = Field(
|
|
48
|
+
default="gemini-2.5-flash-image",
|
|
49
|
+
description="Image model to use for composition.",
|
|
50
|
+
)
|
|
51
|
+
aspect_ratio: Literal["1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"] = Field(
|
|
52
|
+
default="1:1",
|
|
53
|
+
description="Target aspect ratio. Model compatibility is validated automatically.",
|
|
54
|
+
)
|
|
55
|
+
num_variants: int = Field(default=1, description="Number of variants to generate (1-4).")
|
|
56
|
+
|
|
57
|
+
@field_validator("product_name", "text_instruction", "output_file_name")
|
|
58
|
+
@classmethod
|
|
59
|
+
def _not_blank(cls, value: str) -> str:
|
|
60
|
+
if not value.strip():
|
|
61
|
+
raise ValueError("value must not be empty")
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
@field_validator("image_refs")
|
|
65
|
+
@classmethod
|
|
66
|
+
def _validate_refs(cls, value: list[str]) -> list[str]:
|
|
67
|
+
if len(value) < 2:
|
|
68
|
+
raise ValueError("image_refs must include at least two images")
|
|
69
|
+
for item in value:
|
|
70
|
+
if not item.strip():
|
|
71
|
+
raise ValueError("image reference must not be empty")
|
|
72
|
+
return value
|
|
73
|
+
|
|
74
|
+
@field_validator("num_variants")
|
|
75
|
+
@classmethod
|
|
76
|
+
def _validate_variants(cls, value: int) -> int:
|
|
77
|
+
if value < 1 or value > 4:
|
|
78
|
+
raise ValueError("num_variants must be between 1 and 4")
|
|
79
|
+
return value
|
|
80
|
+
|
|
81
|
+
@model_validator(mode="after")
|
|
82
|
+
def _validate_model_aspect_ratio(self) -> "CombineImages":
|
|
83
|
+
validate_aspect_ratio_for_model(self.model, self.aspect_ratio)
|
|
84
|
+
return self
|
|
85
|
+
|
|
86
|
+
def run(self) -> list:
|
|
87
|
+
images_dir = get_images_dir(self.product_name)
|
|
88
|
+
reference_images = [resolve_image_reference(self.product_name, ref)[0] for ref in self.image_refs]
|
|
89
|
+
|
|
90
|
+
if self.model.startswith("gemini-"):
|
|
91
|
+
results, usage_metadata = self._run_gemini(images_dir, reference_images)
|
|
92
|
+
return build_multimodal_outputs(results, "Image composition complete")
|
|
93
|
+
|
|
94
|
+
results, usage_metadata = self._run_openai(images_dir, reference_images)
|
|
95
|
+
return build_multimodal_outputs(results, "Image composition complete")
|
|
96
|
+
|
|
97
|
+
def _run_gemini(self, images_dir, reference_images: list[Image.Image]):
|
|
98
|
+
from google import genai
|
|
99
|
+
from google.genai.types import GenerateContentConfig, ImageConfig
|
|
100
|
+
|
|
101
|
+
api_key = os.getenv("GOOGLE_API_KEY")
|
|
102
|
+
if not api_key:
|
|
103
|
+
raise ValueError("GOOGLE_API_KEY is not set. Add it to your .env to use image composition.")
|
|
104
|
+
|
|
105
|
+
client = genai.Client(api_key=api_key)
|
|
106
|
+
results: list[dict] = []
|
|
107
|
+
total_prompt_tokens = 0.0
|
|
108
|
+
total_candidate_tokens = 0.0
|
|
109
|
+
|
|
110
|
+
def compose_single_variant(idx: int):
|
|
111
|
+
response = client.models.generate_content(
|
|
112
|
+
model=self.model,
|
|
113
|
+
contents=[*reference_images, self.text_instruction],
|
|
114
|
+
config=GenerateContentConfig(
|
|
115
|
+
image_config=ImageConfig(aspect_ratio=self.aspect_ratio),
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
image, usage = extract_gemini_image_and_usage(response)
|
|
119
|
+
if image is None:
|
|
120
|
+
return None
|
|
121
|
+
|
|
122
|
+
variant_name = build_variant_output_name(self.output_file_name, idx, self.num_variants)
|
|
123
|
+
image_name, file_path = save_image(image, variant_name, images_dir)
|
|
124
|
+
return {
|
|
125
|
+
"image_name": image_name,
|
|
126
|
+
"file_path": file_path,
|
|
127
|
+
"preview_b64": image_to_base64_jpeg(image),
|
|
128
|
+
"prompt_tokens": float(usage.get("prompt_token_count") or 0),
|
|
129
|
+
"candidate_tokens": float(usage.get("candidates_token_count") or 0),
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
raw_results = run_parallel_variants_sync(compose_single_variant, self.num_variants)
|
|
133
|
+
if not raw_results:
|
|
134
|
+
raise RuntimeError("Gemini did not return any composed images.")
|
|
135
|
+
|
|
136
|
+
for item in raw_results:
|
|
137
|
+
total_prompt_tokens += item.pop("prompt_tokens")
|
|
138
|
+
total_candidate_tokens += item.pop("candidate_tokens")
|
|
139
|
+
results.append(item)
|
|
140
|
+
|
|
141
|
+
usage_metadata = {
|
|
142
|
+
"prompt_token_count": total_prompt_tokens,
|
|
143
|
+
"candidates_token_count": total_candidate_tokens,
|
|
144
|
+
}
|
|
145
|
+
return results, usage_metadata
|
|
146
|
+
|
|
147
|
+
def _run_openai(self, images_dir, reference_images: list[Image.Image]):
|
|
148
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
|
149
|
+
if not api_key:
|
|
150
|
+
raise RuntimeError("OPENAI_API_KEY is required for OpenAI image composition.")
|
|
151
|
+
|
|
152
|
+
size = get_openai_size_for_aspect_ratio(self.aspect_ratio)
|
|
153
|
+
|
|
154
|
+
input_buffers: list[BytesIO] = []
|
|
155
|
+
for idx, image in enumerate(reference_images, start=1):
|
|
156
|
+
buffer = BytesIO()
|
|
157
|
+
image.save(buffer, format="PNG")
|
|
158
|
+
buffer.seek(0)
|
|
159
|
+
buffer.name = f"reference_{idx}.png"
|
|
160
|
+
input_buffers.append(buffer)
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
client = OpenAI(api_key=api_key)
|
|
164
|
+
response = client.images.edit(
|
|
165
|
+
model=self.model,
|
|
166
|
+
image=input_buffers,
|
|
167
|
+
prompt=self.text_instruction,
|
|
168
|
+
size=size,
|
|
169
|
+
n=self.num_variants,
|
|
170
|
+
)
|
|
171
|
+
finally:
|
|
172
|
+
for buffer in input_buffers:
|
|
173
|
+
buffer.close()
|
|
174
|
+
|
|
175
|
+
images, usage_metadata = extract_openai_images_and_usage(response)
|
|
176
|
+
if not images:
|
|
177
|
+
raise RuntimeError("OpenAI image API did not return composed images.")
|
|
178
|
+
|
|
179
|
+
results: list[dict] = []
|
|
180
|
+
for idx, image in enumerate(images, start=1):
|
|
181
|
+
variant_name = build_variant_output_name(self.output_file_name, idx, len(images))
|
|
182
|
+
image_name, file_path = save_image(image, variant_name, images_dir)
|
|
183
|
+
results.append(
|
|
184
|
+
{
|
|
185
|
+
"image_name": image_name,
|
|
186
|
+
"file_path": file_path,
|
|
187
|
+
"preview_b64": image_to_base64_jpeg(image),
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
return results, usage_metadata
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
# Example test scenario
|
|
195
|
+
tool = CombineImages(
|
|
196
|
+
product_name="Test_Product",
|
|
197
|
+
image_refs=["hero_image_example_oai", "edited_image_example"],
|
|
198
|
+
text_instruction=(
|
|
199
|
+
"Apply logo on a product. Keep the original product image as is."
|
|
200
|
+
),
|
|
201
|
+
output_file_name="combined_example",
|
|
202
|
+
model="gpt-image-1.5",
|
|
203
|
+
aspect_ratio="1:1",
|
|
204
|
+
num_variants=1,
|
|
205
|
+
)
|
|
206
|
+
try:
|
|
207
|
+
result = tool.run()
|
|
208
|
+
print(result)
|
|
209
|
+
except Exception as exc:
|
|
210
|
+
print(f"Image composition failed: {exc}")
|
|
211
|
+
|