@_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,308 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import concurrent.futures
|
|
3
|
+
import io
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
from PIL import Image
|
|
11
|
+
|
|
12
|
+
from agency_swarm import ToolOutputImage, ToolOutputText
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
DEFAULT_EXTENSIONS = (".png", ".jpg", ".jpeg", ".webp")
|
|
16
|
+
ALL_ASPECT_RATIOS = (
|
|
17
|
+
"1:1",
|
|
18
|
+
"2:3",
|
|
19
|
+
"3:2",
|
|
20
|
+
"3:4",
|
|
21
|
+
"4:3",
|
|
22
|
+
"4:5",
|
|
23
|
+
"5:4",
|
|
24
|
+
"9:16",
|
|
25
|
+
"16:9",
|
|
26
|
+
"21:9",
|
|
27
|
+
)
|
|
28
|
+
SUPPORTED_ASPECT_RATIOS_BY_MODEL = {
|
|
29
|
+
"gemini-2.5-flash-image": set(ALL_ASPECT_RATIOS),
|
|
30
|
+
"gemini-3-pro-image-preview": set(ALL_ASPECT_RATIOS),
|
|
31
|
+
# OpenAI image API size options are:
|
|
32
|
+
# 1024x1024 (1:1), 1024x1536 (2:3), 1536x1024 (3:2)
|
|
33
|
+
"gpt-image-1.5": {"1:1", "2:3", "3:2"},
|
|
34
|
+
}
|
|
35
|
+
OPENAI_SIZE_BY_ASPECT_RATIO = {
|
|
36
|
+
"1:1": "1024x1024",
|
|
37
|
+
"2:3": "1024x1536",
|
|
38
|
+
"3:2": "1536x1024",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if os.path.isfile("/.dockerenv"):
|
|
42
|
+
MNT_DIR = Path("/app/mnt")
|
|
43
|
+
else:
|
|
44
|
+
MNT_DIR = Path(__file__).parent.parent.parent.parent / "mnt"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_images_dir(product_name: str) -> Path:
|
|
48
|
+
images_dir = MNT_DIR / product_name / "generated_images"
|
|
49
|
+
images_dir.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
return images_dir
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def find_image_path_from_name(images_dir: Path, image_name: str) -> Path | None:
|
|
54
|
+
for ext in DEFAULT_EXTENSIONS:
|
|
55
|
+
candidate = images_dir / f"{image_name}{ext}"
|
|
56
|
+
if candidate.exists():
|
|
57
|
+
return candidate
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def resolve_image_reference(product_name: str, image_ref: str) -> tuple[Image.Image, str]:
|
|
62
|
+
if not image_ref.strip():
|
|
63
|
+
raise ValueError("image reference must not be empty")
|
|
64
|
+
|
|
65
|
+
parsed = urlparse(image_ref)
|
|
66
|
+
if parsed.scheme in ("http", "https"):
|
|
67
|
+
response = requests.get(image_ref, timeout=30)
|
|
68
|
+
response.raise_for_status()
|
|
69
|
+
image = Image.open(io.BytesIO(response.content))
|
|
70
|
+
return image.convert("RGB"), image_ref
|
|
71
|
+
|
|
72
|
+
candidate_path = Path(image_ref).expanduser().resolve()
|
|
73
|
+
if candidate_path.exists():
|
|
74
|
+
image = Image.open(candidate_path)
|
|
75
|
+
return image.convert("RGB"), str(candidate_path)
|
|
76
|
+
|
|
77
|
+
images_dir = get_images_dir(product_name)
|
|
78
|
+
by_name = find_image_path_from_name(images_dir, image_ref)
|
|
79
|
+
if by_name is not None:
|
|
80
|
+
image = Image.open(by_name)
|
|
81
|
+
return image.convert("RGB"), str(by_name)
|
|
82
|
+
|
|
83
|
+
raise FileNotFoundError(
|
|
84
|
+
f"Could not resolve image reference '{image_ref}' as URL, path, or name in {images_dir}."
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
_IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".tiff"}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def normalize_file_name(value: str) -> str:
|
|
92
|
+
"""Strip a known image extension from the name, but leave other dots untouched.
|
|
93
|
+
|
|
94
|
+
"hero.png" → "hero"
|
|
95
|
+
"5.1_no_bg" → "5.1_no_bg" (dot is part of the name, not an extension)
|
|
96
|
+
"""
|
|
97
|
+
name = value.strip()
|
|
98
|
+
if not name:
|
|
99
|
+
raise ValueError("file name must not be empty")
|
|
100
|
+
p = Path(name)
|
|
101
|
+
return p.stem if p.suffix.lower() in _IMAGE_EXTENSIONS else name
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def build_variant_output_name(output_name: str, variant_index: int, total_variants: int) -> str:
|
|
105
|
+
raw_value = output_name.strip()
|
|
106
|
+
if not raw_value:
|
|
107
|
+
raise ValueError("output name must not be empty")
|
|
108
|
+
if total_variants <= 1:
|
|
109
|
+
return raw_value
|
|
110
|
+
|
|
111
|
+
candidate = Path(raw_value)
|
|
112
|
+
has_image_ext = candidate.suffix.lower() in _IMAGE_EXTENSIONS
|
|
113
|
+
if has_image_ext:
|
|
114
|
+
variant_file = f"{candidate.stem}_variant_{variant_index}{candidate.suffix}"
|
|
115
|
+
return str(candidate.with_name(variant_file))
|
|
116
|
+
|
|
117
|
+
return f"{raw_value}_variant_{variant_index}"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def save_image(image: Image.Image, output_name: str, images_dir: Path) -> tuple[str, str]:
|
|
121
|
+
raw_value = output_name.strip()
|
|
122
|
+
candidate = Path(raw_value).expanduser()
|
|
123
|
+
|
|
124
|
+
has_image_ext = candidate.suffix.lower() in _IMAGE_EXTENSIONS
|
|
125
|
+
has_path_sep = any(sep in raw_value for sep in ("/", "\\"))
|
|
126
|
+
|
|
127
|
+
if has_image_ext:
|
|
128
|
+
output_path = candidate if candidate.is_absolute() else images_dir / candidate
|
|
129
|
+
image_name = output_path.stem
|
|
130
|
+
elif has_path_sep:
|
|
131
|
+
output_path = candidate if candidate.is_absolute() else images_dir / candidate
|
|
132
|
+
output_path = output_path.with_suffix(".png")
|
|
133
|
+
image_name = output_path.stem
|
|
134
|
+
else:
|
|
135
|
+
image_name = normalize_file_name(raw_value)
|
|
136
|
+
output_path = images_dir / f"{image_name}.png"
|
|
137
|
+
|
|
138
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
139
|
+
image.save(output_path, format="PNG")
|
|
140
|
+
return image_name, str(output_path)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def image_to_base64_jpeg(
|
|
144
|
+
image: Image.Image,
|
|
145
|
+
max_size: int = 768,
|
|
146
|
+
target_bytes: int = 120_000,
|
|
147
|
+
min_quality: int = 45,
|
|
148
|
+
) -> str:
|
|
149
|
+
"""
|
|
150
|
+
Create a compact JPEG preview for multimodal outputs.
|
|
151
|
+
|
|
152
|
+
This intentionally optimizes for small payload size to reduce token costs.
|
|
153
|
+
"""
|
|
154
|
+
rgb = image.convert("RGB")
|
|
155
|
+
width, height = rgb.size
|
|
156
|
+
|
|
157
|
+
if max(width, height) > max_size:
|
|
158
|
+
scale = max_size / max(width, height)
|
|
159
|
+
rgb = rgb.resize((int(width * scale), int(height * scale)), Image.Resampling.LANCZOS)
|
|
160
|
+
|
|
161
|
+
best_bytes: bytes | None = None
|
|
162
|
+
work = rgb
|
|
163
|
+
quality_steps = (80, 70, 62, 55, min_quality)
|
|
164
|
+
|
|
165
|
+
# Try lowering JPEG quality first.
|
|
166
|
+
for quality in quality_steps:
|
|
167
|
+
buffer = io.BytesIO()
|
|
168
|
+
work.save(buffer, format="JPEG", quality=quality, optimize=True, progressive=True)
|
|
169
|
+
candidate = buffer.getvalue()
|
|
170
|
+
if best_bytes is None or len(candidate) < len(best_bytes):
|
|
171
|
+
best_bytes = candidate
|
|
172
|
+
if len(candidate) <= target_bytes:
|
|
173
|
+
return base64.b64encode(candidate).decode("utf-8")
|
|
174
|
+
|
|
175
|
+
# If still too large, iteratively downscale and retry quality steps.
|
|
176
|
+
while max(work.size) > 320:
|
|
177
|
+
next_size = (max(1, int(work.width * 0.85)), max(1, int(work.height * 0.85)))
|
|
178
|
+
work = work.resize(next_size, Image.Resampling.LANCZOS)
|
|
179
|
+
for quality in quality_steps:
|
|
180
|
+
buffer = io.BytesIO()
|
|
181
|
+
work.save(buffer, format="JPEG", quality=quality, optimize=True, progressive=True)
|
|
182
|
+
candidate = buffer.getvalue()
|
|
183
|
+
if best_bytes is None or len(candidate) < len(best_bytes):
|
|
184
|
+
best_bytes = candidate
|
|
185
|
+
if len(candidate) <= target_bytes:
|
|
186
|
+
return base64.b64encode(candidate).decode("utf-8")
|
|
187
|
+
|
|
188
|
+
# Final fallback: return the smallest candidate we produced.
|
|
189
|
+
assert best_bytes is not None
|
|
190
|
+
return base64.b64encode(best_bytes).decode("utf-8")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def build_multimodal_outputs(items: list[dict[str, Any]], title: str) -> list:
|
|
194
|
+
lines = [f"{title}: {len(items)} image(s)"]
|
|
195
|
+
for item in items:
|
|
196
|
+
lines.append(f"- {item['image_name']}: {item['file_path']}")
|
|
197
|
+
|
|
198
|
+
outputs: list = [ToolOutputText(type="text", text="\n".join(lines))]
|
|
199
|
+
for item in items:
|
|
200
|
+
outputs.append(ToolOutputText(type="text", text=f"Path: {item['file_path']}"))
|
|
201
|
+
outputs.append(
|
|
202
|
+
ToolOutputImage(
|
|
203
|
+
type="image",
|
|
204
|
+
image_url=f"data:image/jpeg;base64,{item['preview_b64']}",
|
|
205
|
+
detail="auto",
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
return outputs
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def extract_gemini_image_and_usage(response: Any) -> tuple[Image.Image | None, dict]:
|
|
212
|
+
image: Image.Image | None = None
|
|
213
|
+
usage = getattr(response, "usage_metadata", {}) or {}
|
|
214
|
+
|
|
215
|
+
if usage and not isinstance(usage, dict):
|
|
216
|
+
if hasattr(usage, "model_dump"):
|
|
217
|
+
usage = usage.model_dump()
|
|
218
|
+
elif hasattr(usage, "__dict__"):
|
|
219
|
+
usage = vars(usage)
|
|
220
|
+
else:
|
|
221
|
+
try:
|
|
222
|
+
usage = dict(usage)
|
|
223
|
+
except (TypeError, ValueError):
|
|
224
|
+
usage = {}
|
|
225
|
+
|
|
226
|
+
candidates = getattr(response, "candidates", None) or []
|
|
227
|
+
for candidate in candidates:
|
|
228
|
+
content = getattr(candidate, "content", None)
|
|
229
|
+
parts = getattr(content, "parts", None) or []
|
|
230
|
+
for part in parts:
|
|
231
|
+
inline_data = getattr(part, "inline_data", None)
|
|
232
|
+
if inline_data and getattr(inline_data, "data", None):
|
|
233
|
+
image = Image.open(io.BytesIO(inline_data.data)).convert("RGB")
|
|
234
|
+
return image, usage
|
|
235
|
+
|
|
236
|
+
parts = getattr(response, "parts", None) or []
|
|
237
|
+
for part in parts:
|
|
238
|
+
inline_data = getattr(part, "inline_data", None)
|
|
239
|
+
if inline_data and getattr(inline_data, "data", None):
|
|
240
|
+
image = Image.open(io.BytesIO(inline_data.data)).convert("RGB")
|
|
241
|
+
return image, usage
|
|
242
|
+
|
|
243
|
+
return None, usage
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def extract_openai_images_and_usage(response: Any) -> tuple[list[Image.Image], dict]:
|
|
247
|
+
usage = getattr(response, "usage", {}) or {}
|
|
248
|
+
if usage and not isinstance(usage, dict):
|
|
249
|
+
if hasattr(usage, "model_dump"):
|
|
250
|
+
usage = usage.model_dump()
|
|
251
|
+
elif hasattr(usage, "__dict__"):
|
|
252
|
+
usage = vars(usage)
|
|
253
|
+
else:
|
|
254
|
+
try:
|
|
255
|
+
usage = dict(usage)
|
|
256
|
+
except (TypeError, ValueError):
|
|
257
|
+
usage = {}
|
|
258
|
+
|
|
259
|
+
images: list[Image.Image] = []
|
|
260
|
+
data = getattr(response, "data", None) or []
|
|
261
|
+
for item in data:
|
|
262
|
+
b64 = getattr(item, "b64_json", None)
|
|
263
|
+
if b64:
|
|
264
|
+
raw = base64.b64decode(b64)
|
|
265
|
+
images.append(Image.open(io.BytesIO(raw)).convert("RGB"))
|
|
266
|
+
|
|
267
|
+
return images, usage
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def run_parallel_variants_sync(task_fn, num_variants: int) -> list:
|
|
271
|
+
results_by_index: dict[int, Any] = {}
|
|
272
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=num_variants) as executor:
|
|
273
|
+
future_to_index = {
|
|
274
|
+
executor.submit(task_fn, idx): idx
|
|
275
|
+
for idx in range(1, num_variants + 1)
|
|
276
|
+
}
|
|
277
|
+
for future in concurrent.futures.as_completed(future_to_index):
|
|
278
|
+
idx = future_to_index[future]
|
|
279
|
+
try:
|
|
280
|
+
result = future.result()
|
|
281
|
+
if result is not None:
|
|
282
|
+
results_by_index[idx] = result
|
|
283
|
+
except Exception:
|
|
284
|
+
pass
|
|
285
|
+
|
|
286
|
+
return [results_by_index[idx] for idx in sorted(results_by_index.keys())]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def validate_aspect_ratio_for_model(model: str, aspect_ratio: str) -> None:
|
|
290
|
+
supported = SUPPORTED_ASPECT_RATIOS_BY_MODEL.get(model)
|
|
291
|
+
if supported is None:
|
|
292
|
+
raise ValueError(f"Unsupported model: {model}")
|
|
293
|
+
if aspect_ratio not in supported:
|
|
294
|
+
raise ValueError(
|
|
295
|
+
f"Aspect ratio '{aspect_ratio}' is not supported by model '{model}'. "
|
|
296
|
+
f"Supported values: {sorted(supported)}"
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def get_openai_size_for_aspect_ratio(aspect_ratio: str) -> str:
|
|
301
|
+
size = OPENAI_SIZE_BY_ASPECT_RATIO.get(aspect_ratio)
|
|
302
|
+
if not size:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Aspect ratio '{aspect_ratio}' cannot be mapped to an OpenAI image size. "
|
|
305
|
+
f"Supported values: {sorted(OPENAI_SIZE_BY_ASPECT_RATIO.keys())}"
|
|
306
|
+
)
|
|
307
|
+
return size
|
|
308
|
+
|
package/onboard.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""OpenSwarm interactive setup wizard.
|
|
3
|
+
|
|
4
|
+
Run directly: python onboard.py
|
|
5
|
+
Auto-launched: python run.py (when no provider key is found)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import getpass
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from dotenv import dotenv_values, set_key
|
|
13
|
+
from rich import box
|
|
14
|
+
from rich.console import Console
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.rule import Rule
|
|
17
|
+
from rich.table import Table
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
import questionary
|
|
21
|
+
from questionary import Choice, Style as QStyle
|
|
22
|
+
import questionary.prompts.common as _qc_common
|
|
23
|
+
|
|
24
|
+
# Swap filled circle → checkmark for selected state.
|
|
25
|
+
_qc_common.INDICATOR_SELECTED = "✓"
|
|
26
|
+
|
|
27
|
+
_HAS_QUESTIONARY = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
_HAS_QUESTIONARY = False
|
|
30
|
+
|
|
31
|
+
console = Console()
|
|
32
|
+
|
|
33
|
+
ENV_PATH = Path(__file__).parent / ".env"
|
|
34
|
+
|
|
35
|
+
# ── questionary theme ─────────────────────────────────────────────────────────
|
|
36
|
+
_QSTYLE = None
|
|
37
|
+
if _HAS_QUESTIONARY:
|
|
38
|
+
_QSTYLE = QStyle([
|
|
39
|
+
("qmark", "fg:#4fc3f7 bold"),
|
|
40
|
+
("question", "bold"),
|
|
41
|
+
("answer", "fg:#4fc3f7 bold"),
|
|
42
|
+
("pointer", "fg:#4fc3f7 bold noreverse"),
|
|
43
|
+
("highlighted", "noreverse"),
|
|
44
|
+
("selected", "fg:#4fc3f7 bold noreverse"),
|
|
45
|
+
("separator", "fg:#555555 noreverse"),
|
|
46
|
+
("instruction", "fg:#555555 italic noreverse"),
|
|
47
|
+
("text", "noreverse"),
|
|
48
|
+
])
|
|
49
|
+
|
|
50
|
+
# ── provider definitions ──────────────────────────────────────────────────────
|
|
51
|
+
PROVIDERS = [
|
|
52
|
+
{
|
|
53
|
+
"name": "OpenAI",
|
|
54
|
+
"env_key": "OPENAI_API_KEY",
|
|
55
|
+
"default_model": "gpt-5.2",
|
|
56
|
+
"url": "https://platform.openai.com/api-keys",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "Anthropic",
|
|
60
|
+
"env_key": "ANTHROPIC_API_KEY",
|
|
61
|
+
"default_model": "litellm/claude-sonnet-4-6",
|
|
62
|
+
"url": "https://console.anthropic.com/settings/keys",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "Google Gemini",
|
|
66
|
+
"env_key": "GOOGLE_API_KEY",
|
|
67
|
+
"default_model": "litellm/gemini/gemini-3-flash",
|
|
68
|
+
"url": "https://aistudio.google.com/app/apikey",
|
|
69
|
+
},
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
# ── add-on definitions ────────────────────────────────────────────────────────
|
|
73
|
+
# exclude_for: list of provider names that already cover this key
|
|
74
|
+
ADD_ONS = [
|
|
75
|
+
{
|
|
76
|
+
"id": "search",
|
|
77
|
+
"name": "Web Search",
|
|
78
|
+
"description": "Web, Scholar & product search for all agents",
|
|
79
|
+
"keys": [
|
|
80
|
+
{"env": "SEARCH_API_KEY", "label": "SearchAPI key",
|
|
81
|
+
"url": "https://www.searchapi.io"},
|
|
82
|
+
],
|
|
83
|
+
"exclude_for": [],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "anthropic",
|
|
87
|
+
"name": "Anthropic Claude — better slides quality",
|
|
88
|
+
"description": "Claude produces significantly better slide HTML output",
|
|
89
|
+
"keys": [
|
|
90
|
+
{"env": "ANTHROPIC_API_KEY", "label": "Anthropic API key",
|
|
91
|
+
"url": "https://console.anthropic.com/settings/keys"},
|
|
92
|
+
],
|
|
93
|
+
"exclude_for": ["Anthropic"],
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"id": "composio",
|
|
97
|
+
"name": "Composio — 10,000+ integrations",
|
|
98
|
+
"description": "Gmail, Slack, GitHub, HubSpot, Google Calendar and more",
|
|
99
|
+
"keys": [
|
|
100
|
+
{"env": "COMPOSIO_API_KEY", "label": "Composio API key",
|
|
101
|
+
"url": "https://composio.dev"},
|
|
102
|
+
{"env": "COMPOSIO_USER_ID", "label": "Composio user ID",
|
|
103
|
+
"url": "https://composio.dev"},
|
|
104
|
+
],
|
|
105
|
+
"exclude_for": [],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"id": "google",
|
|
109
|
+
"name": "Google Gemini — image gen & Veo video",
|
|
110
|
+
"description": "Gemini image generation/editing and Veo video generation",
|
|
111
|
+
"keys": [
|
|
112
|
+
{"env": "GOOGLE_API_KEY", "label": "Google AI API key",
|
|
113
|
+
"url": "https://aistudio.google.com/app/apikey"},
|
|
114
|
+
],
|
|
115
|
+
"exclude_for": ["Google Gemini"],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"id": "fal",
|
|
119
|
+
"name": "Fal.ai — Seedance video & background removal",
|
|
120
|
+
"description": "Seedance 1.5 Pro video gen, video editing, background removal",
|
|
121
|
+
"keys": [
|
|
122
|
+
{"env": "FAL_KEY", "label": "Fal.ai API key",
|
|
123
|
+
"url": "https://fal.ai/dashboard/keys"},
|
|
124
|
+
],
|
|
125
|
+
"exclude_for": [],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"id": "stock",
|
|
129
|
+
"name": "Stock photos — Pexels / Pixabay / Unsplash",
|
|
130
|
+
"description": "Image search for the Slides Agent",
|
|
131
|
+
"keys": [
|
|
132
|
+
{"env": "PEXELS_API_KEY", "label": "Pexels API key",
|
|
133
|
+
"url": "https://www.pexels.com/api"},
|
|
134
|
+
{"env": "PIXABAY_API_KEY", "label": "Pixabay API key",
|
|
135
|
+
"url": "https://pixabay.com/api/docs"},
|
|
136
|
+
{"env": "UNSPLASH_ACCESS_KEY", "label": "Unsplash access key",
|
|
137
|
+
"url": "https://unsplash.com/developers"},
|
|
138
|
+
],
|
|
139
|
+
"exclude_for": [],
|
|
140
|
+
},
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
# ── ui helpers ────────────────────────────────────────────────────────────────
|
|
144
|
+
|
|
145
|
+
def _step(n: int, label: str) -> None:
|
|
146
|
+
console.print()
|
|
147
|
+
console.print(Rule(f"[bold]Step {n} · {label}[/bold]", style="cyan"))
|
|
148
|
+
console.print()
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _ask_select(message: str, choices: list) -> object:
|
|
152
|
+
if _HAS_QUESTIONARY:
|
|
153
|
+
return questionary.select(message, choices=choices, style=_QSTYLE).ask()
|
|
154
|
+
# plain fallback
|
|
155
|
+
titles = [c.title if isinstance(c, Choice) else c for c in choices]
|
|
156
|
+
values = [c.value if isinstance(c, Choice) else c for c in choices]
|
|
157
|
+
console.print(f"\n[bold]{message}[/bold]")
|
|
158
|
+
for i, title in enumerate(titles, 1):
|
|
159
|
+
console.print(f" [cyan]{i}.[/cyan] {title}")
|
|
160
|
+
while True:
|
|
161
|
+
raw = input("Enter number: ").strip()
|
|
162
|
+
if raw.isdigit() and 1 <= int(raw) <= len(titles):
|
|
163
|
+
return values[int(raw) - 1]
|
|
164
|
+
console.print("[red]Invalid choice, try again.[/red]")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _ask_checkbox(message: str, choices: list) -> list:
|
|
168
|
+
if _HAS_QUESTIONARY:
|
|
169
|
+
return questionary.checkbox(message, choices=choices, style=_QSTYLE, pointer="❯").ask() or []
|
|
170
|
+
# plain fallback — comma-separated numbers
|
|
171
|
+
titles = [c.title if isinstance(c, Choice) else c for c in choices]
|
|
172
|
+
values = [c.value if isinstance(c, Choice) else c for c in choices]
|
|
173
|
+
console.print(f"\n[bold]{message}[/bold]")
|
|
174
|
+
console.print("[dim] Enter comma-separated numbers, or press Enter to skip[/dim]")
|
|
175
|
+
for i, title in enumerate(titles, 1):
|
|
176
|
+
console.print(f" [cyan]{i}.[/cyan] {title}")
|
|
177
|
+
raw = input("Selection: ").strip()
|
|
178
|
+
if not raw:
|
|
179
|
+
return []
|
|
180
|
+
result = []
|
|
181
|
+
for part in raw.split(","):
|
|
182
|
+
part = part.strip()
|
|
183
|
+
if part.isdigit() and 1 <= int(part) <= len(titles):
|
|
184
|
+
result.append(values[int(part) - 1])
|
|
185
|
+
return result
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _ask_secret(label: str, url: str) -> str:
|
|
189
|
+
console.print(f" [dim]Get yours at[/dim] [link={url}]{url}[/link]")
|
|
190
|
+
if _HAS_QUESTIONARY:
|
|
191
|
+
val = questionary.password(f" {label}: ", style=_QSTYLE).ask()
|
|
192
|
+
return (val or "").strip()
|
|
193
|
+
return getpass.getpass(f" {label}: ").strip()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _ask_confirm(message: str, default: bool = True) -> bool:
|
|
197
|
+
if _HAS_QUESTIONARY:
|
|
198
|
+
return questionary.confirm(message, default=default, style=_QSTYLE).ask()
|
|
199
|
+
prompt = f"{message} [{'Y/n' if default else 'y/N'}]: "
|
|
200
|
+
raw = input(prompt).strip().lower()
|
|
201
|
+
return default if not raw else raw in ("y", "yes")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _write_env(updates: dict) -> None:
|
|
205
|
+
if not ENV_PATH.exists():
|
|
206
|
+
ENV_PATH.write_text("", encoding="utf-8")
|
|
207
|
+
for key, value in updates.items():
|
|
208
|
+
if value:
|
|
209
|
+
set_key(str(ENV_PATH), key, value)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ── main wizard ───────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
def run_onboarding() -> None:
|
|
215
|
+
console.print()
|
|
216
|
+
console.print(Panel.fit(
|
|
217
|
+
"[bold cyan]OpenSwarm[/bold cyan] [dim]— open-source multi-agent AI team[/dim]\n"
|
|
218
|
+
"[dim]Let's get you set up in a few steps.[/dim]",
|
|
219
|
+
border_style="cyan",
|
|
220
|
+
padding=(1, 4),
|
|
221
|
+
))
|
|
222
|
+
|
|
223
|
+
existing = dotenv_values(str(ENV_PATH)) if ENV_PATH.exists() else {}
|
|
224
|
+
updates: dict[str, str] = {}
|
|
225
|
+
|
|
226
|
+
# ── Step 1: provider ──────────────────────────────────────────────────────
|
|
227
|
+
_step(1, "AI Provider")
|
|
228
|
+
|
|
229
|
+
provider_choices = [
|
|
230
|
+
Choice(title=p["name"], value=p)
|
|
231
|
+
for p in PROVIDERS
|
|
232
|
+
]
|
|
233
|
+
provider = _ask_select("Choose your primary AI provider:", provider_choices)
|
|
234
|
+
|
|
235
|
+
# ── Step 2: API key ───────────────────────────────────────────────────────
|
|
236
|
+
_step(2, "API Key")
|
|
237
|
+
|
|
238
|
+
existing_key = existing.get(provider["env_key"], "")
|
|
239
|
+
if existing_key:
|
|
240
|
+
console.print(f" [dim]{provider['env_key']} is already configured.[/dim]")
|
|
241
|
+
if _ask_confirm(" Update it?", default=False):
|
|
242
|
+
key = _ask_secret(f"{provider['name']} API key", provider["url"])
|
|
243
|
+
updates[provider["env_key"]] = key or existing_key
|
|
244
|
+
else:
|
|
245
|
+
updates[provider["env_key"]] = existing_key
|
|
246
|
+
else:
|
|
247
|
+
key = _ask_secret(f"{provider['name']} API key", provider["url"])
|
|
248
|
+
if key:
|
|
249
|
+
updates[provider["env_key"]] = key
|
|
250
|
+
|
|
251
|
+
updates["DEFAULT_MODEL"] = provider["default_model"]
|
|
252
|
+
|
|
253
|
+
# ── Step 3: add-ons ───────────────────────────────────────────────────────
|
|
254
|
+
_step(3, "Add-ons [dim](optional)[/dim]")
|
|
255
|
+
|
|
256
|
+
available = [a for a in ADD_ONS if provider["name"] not in a["exclude_for"]]
|
|
257
|
+
addon_choices = [
|
|
258
|
+
Choice(
|
|
259
|
+
title=(
|
|
260
|
+
[
|
|
261
|
+
("class:text", a["name"]),
|
|
262
|
+
("fg:#555555", " · "),
|
|
263
|
+
("fg:#666666", a["description"]),
|
|
264
|
+
]
|
|
265
|
+
if _HAS_QUESTIONARY
|
|
266
|
+
else f"{a['name']} — {a['description']}"
|
|
267
|
+
),
|
|
268
|
+
value=a["id"],
|
|
269
|
+
)
|
|
270
|
+
for a in available
|
|
271
|
+
]
|
|
272
|
+
selected_ids = _ask_checkbox("Select add-ons to enable:", addon_choices)
|
|
273
|
+
selected_addons = [a for a in available if a["id"] in selected_ids]
|
|
274
|
+
|
|
275
|
+
# ── Step 4: add-on keys ───────────────────────────────────────────────────
|
|
276
|
+
if selected_addons:
|
|
277
|
+
_step(4, "Add-on Keys")
|
|
278
|
+
for addon in selected_addons:
|
|
279
|
+
console.print(f"\n [bold]{addon['name'].split(' ')[0]}[/bold]")
|
|
280
|
+
for key_spec in addon["keys"]:
|
|
281
|
+
existing_val = existing.get(key_spec["env"], "")
|
|
282
|
+
if existing_val:
|
|
283
|
+
console.print(f" [dim]{key_spec['env']} is already configured.[/dim]")
|
|
284
|
+
if not _ask_confirm(" Update it?", default=False):
|
|
285
|
+
updates[key_spec["env"]] = existing_val
|
|
286
|
+
continue
|
|
287
|
+
val = _ask_secret(key_spec["label"], key_spec["url"])
|
|
288
|
+
if val:
|
|
289
|
+
updates[key_spec["env"]] = val
|
|
290
|
+
|
|
291
|
+
# ── write .env ────────────────────────────────────────────────────────────
|
|
292
|
+
_write_env(updates)
|
|
293
|
+
|
|
294
|
+
# ── summary ───────────────────────────────────────────────────────────────
|
|
295
|
+
console.print()
|
|
296
|
+
console.print(Rule("[bold green]Setup complete[/bold green]", style="green"))
|
|
297
|
+
console.print()
|
|
298
|
+
|
|
299
|
+
table = Table(box=box.SIMPLE, show_header=False, padding=(0, 2))
|
|
300
|
+
table.add_column(style="dim", no_wrap=True)
|
|
301
|
+
table.add_column()
|
|
302
|
+
table.add_row("Provider", f"[cyan]{provider['name']}[/cyan]")
|
|
303
|
+
table.add_row("Model", f"[cyan]{provider['default_model']}[/cyan]")
|
|
304
|
+
table.add_row(".env", f"[cyan]{ENV_PATH}[/cyan]")
|
|
305
|
+
saved = [k for k, v in updates.items() if v and not k.startswith("DEFAULT_")]
|
|
306
|
+
if saved:
|
|
307
|
+
table.add_row("Keys saved", f"[cyan]{', '.join(saved)}[/cyan]")
|
|
308
|
+
console.print(table)
|
|
309
|
+
|
|
310
|
+
console.print()
|
|
311
|
+
console.print(Panel(
|
|
312
|
+
"[bold]python swarm.py[/bold] [dim]launch interactive terminal[/dim]\n"
|
|
313
|
+
"[bold]python server.py[/bold] [dim]start the API server[/dim]",
|
|
314
|
+
border_style="green",
|
|
315
|
+
padding=(0, 3),
|
|
316
|
+
))
|
|
317
|
+
console.print()
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
if __name__ == "__main__":
|
|
321
|
+
try:
|
|
322
|
+
run_onboarding()
|
|
323
|
+
except KeyboardInterrupt:
|
|
324
|
+
console.print("\n\n[dim]Setup cancelled.[/dim]\n")
|
|
325
|
+
sys.exit(0)
|
|
Binary file
|
|
Binary file
|