@_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,94 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from agency_swarm.tools import BaseTool
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ListSkills(BaseTool):
|
|
7
|
+
"""
|
|
8
|
+
Lists all skills currently available to you.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def run(self):
|
|
12
|
+
try:
|
|
13
|
+
skills_path = os.path.join(os.getcwd(), "mnt/skills")
|
|
14
|
+
|
|
15
|
+
if not os.path.exists(skills_path):
|
|
16
|
+
return f"Error: Skills folder does not exist: {skills_path}"
|
|
17
|
+
|
|
18
|
+
if not os.path.isdir(skills_path):
|
|
19
|
+
return f"Error: Skills path is not a directory: {skills_path}"
|
|
20
|
+
|
|
21
|
+
skills = []
|
|
22
|
+
try:
|
|
23
|
+
entries = os.listdir(skills_path)
|
|
24
|
+
except PermissionError:
|
|
25
|
+
return f"Error: Permission denied accessing skills folder: {skills_path}"
|
|
26
|
+
|
|
27
|
+
for entry in sorted(entries):
|
|
28
|
+
entry_path = os.path.join(skills_path, entry)
|
|
29
|
+
|
|
30
|
+
if not os.path.isdir(entry_path):
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
skill_file = None
|
|
34
|
+
if os.path.exists(os.path.join(entry_path, "SKILL.md")):
|
|
35
|
+
skill_file = os.path.join(entry_path, "SKILL.md")
|
|
36
|
+
elif os.path.exists(os.path.join(entry_path, "skill.md")):
|
|
37
|
+
skill_file = os.path.join(entry_path, "skill.md")
|
|
38
|
+
|
|
39
|
+
if not skill_file:
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
with open(skill_file, "r", encoding="utf-8") as f:
|
|
44
|
+
lines = f.readlines()
|
|
45
|
+
|
|
46
|
+
name = None
|
|
47
|
+
description = None
|
|
48
|
+
|
|
49
|
+
# Parse frontmatter: lines 2 and 3 carry "name:" and "description:"
|
|
50
|
+
if len(lines) > 1:
|
|
51
|
+
line2 = lines[1].strip()
|
|
52
|
+
if line2.startswith("name:"):
|
|
53
|
+
name = line2.split("name:", 1)[1].strip()
|
|
54
|
+
|
|
55
|
+
if len(lines) > 2:
|
|
56
|
+
line3 = lines[2].strip()
|
|
57
|
+
if line3.startswith("description:"):
|
|
58
|
+
description = line3.split("description:", 1)[1].strip()
|
|
59
|
+
|
|
60
|
+
skills.append({
|
|
61
|
+
"name": name or entry,
|
|
62
|
+
"description": description or "No description available",
|
|
63
|
+
"relative_path": os.path.relpath(skill_file, os.getcwd()),
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
except Exception as e:
|
|
67
|
+
skills.append({
|
|
68
|
+
"name": entry,
|
|
69
|
+
"description": f"Error reading skill file: {str(e)}",
|
|
70
|
+
"relative_path": os.path.relpath(skill_file, os.getcwd()),
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
if not skills:
|
|
74
|
+
return f"No skills found in {skills_path}"
|
|
75
|
+
|
|
76
|
+
output = [f"Found {len(skills)} skill(s) in {skills_path}:\n"]
|
|
77
|
+
|
|
78
|
+
for i, skill in enumerate(skills, 1):
|
|
79
|
+
output.append(f"\n{i}. {skill['name']}")
|
|
80
|
+
output.append(f" Description: {skill['description']}")
|
|
81
|
+
output.append(f" Path: {skill['relative_path']}")
|
|
82
|
+
|
|
83
|
+
return "\n".join(output)
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
return f"Error listing skills: {str(e)}"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
# Test the tool
|
|
91
|
+
tool = ListSkills()
|
|
92
|
+
result = tool.run()
|
|
93
|
+
print(result)
|
|
94
|
+
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
from typing import Literal, Optional
|
|
2
|
+
from agency_swarm.tools import BaseTool
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
from helpers import execute_composio_tool
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ManageLabels(BaseTool):
|
|
10
|
+
"""
|
|
11
|
+
Manages email labels (Gmail) or categories (Outlook).
|
|
12
|
+
|
|
13
|
+
Actions:
|
|
14
|
+
- list: List all labels/categories
|
|
15
|
+
- create: Create a new label/category
|
|
16
|
+
- update: Rename or update a label (Gmail only)
|
|
17
|
+
- delete: Delete a label/category
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
provider: Literal["gmail", "outlook"] = Field(
|
|
21
|
+
...,
|
|
22
|
+
description="Email provider: 'gmail' or 'outlook'"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
action: Literal["list", "create", "update", "delete"] = Field(
|
|
26
|
+
...,
|
|
27
|
+
description="Action to perform: 'list', 'create', 'update', or 'delete'"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
label_name: Optional[str] = Field(
|
|
31
|
+
default=None,
|
|
32
|
+
description="Name of the label/category (required for create, update)"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
label_id: Optional[str] = Field(
|
|
36
|
+
default=None,
|
|
37
|
+
description="ID of the label/category (required for update, delete). For Gmail, use format 'Label_123'"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
new_name: Optional[str] = Field(
|
|
41
|
+
default=None,
|
|
42
|
+
description="New name when updating a label (Gmail only)"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
color: Optional[str] = Field(
|
|
46
|
+
default=None,
|
|
47
|
+
description="Color for the label. Gmail: hex color like '#fb4c2f'. Outlook: 'preset0' through 'preset24'"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def run(self):
|
|
51
|
+
try:
|
|
52
|
+
if self.provider == "gmail":
|
|
53
|
+
return self._manage_gmail_labels(execute_composio_tool)
|
|
54
|
+
else:
|
|
55
|
+
return self._manage_outlook_categories(execute_composio_tool)
|
|
56
|
+
|
|
57
|
+
except Exception as e:
|
|
58
|
+
return f"Error managing labels: {str(e)}"
|
|
59
|
+
|
|
60
|
+
def _manage_gmail_labels(self, execute_tool) -> str:
|
|
61
|
+
"""Manages Gmail labels."""
|
|
62
|
+
if self.action == "list":
|
|
63
|
+
return self._list_gmail_labels(execute_tool)
|
|
64
|
+
elif self.action == "create":
|
|
65
|
+
return self._create_gmail_label(execute_tool)
|
|
66
|
+
elif self.action == "update":
|
|
67
|
+
return self._update_gmail_label(execute_tool)
|
|
68
|
+
elif self.action == "delete":
|
|
69
|
+
return self._delete_gmail_label(execute_tool)
|
|
70
|
+
else:
|
|
71
|
+
return f"Unknown action: {self.action}"
|
|
72
|
+
|
|
73
|
+
def _list_gmail_labels(self, execute_tool) -> str:
|
|
74
|
+
"""Lists all Gmail labels."""
|
|
75
|
+
result = execute_tool(
|
|
76
|
+
tool_name="GMAIL_LIST_LABELS",
|
|
77
|
+
arguments={"user_id": "me"},
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if isinstance(result, dict) and result.get("error"):
|
|
81
|
+
return f"Error listing Gmail labels: {result.get('error')}"
|
|
82
|
+
|
|
83
|
+
labels = result.get("data", {}).get("labels", [])
|
|
84
|
+
|
|
85
|
+
formatted_labels = []
|
|
86
|
+
for label in labels:
|
|
87
|
+
formatted_labels.append({
|
|
88
|
+
"id": label.get("id"),
|
|
89
|
+
"name": label.get("name"),
|
|
90
|
+
"type": label.get("type"), # system or user
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
return json.dumps({
|
|
94
|
+
"provider": "gmail",
|
|
95
|
+
"count": len(formatted_labels),
|
|
96
|
+
"labels": formatted_labels
|
|
97
|
+
}, indent=2)
|
|
98
|
+
|
|
99
|
+
def _create_gmail_label(self, execute_tool) -> str:
|
|
100
|
+
"""Creates a Gmail label."""
|
|
101
|
+
if not self.label_name:
|
|
102
|
+
return "Error: label_name is required for create action"
|
|
103
|
+
|
|
104
|
+
arguments = {
|
|
105
|
+
"user_id": "me",
|
|
106
|
+
"label_name": self.label_name
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if self.color:
|
|
110
|
+
arguments["background_color"] = self.color
|
|
111
|
+
|
|
112
|
+
result = execute_tool(
|
|
113
|
+
tool_name="GMAIL_CREATE_LABEL",
|
|
114
|
+
arguments=arguments,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if isinstance(result, dict) and result.get("error"):
|
|
118
|
+
return f"Error creating Gmail label: {result.get('error')}"
|
|
119
|
+
|
|
120
|
+
data = result.get("data", {})
|
|
121
|
+
|
|
122
|
+
return json.dumps({
|
|
123
|
+
"provider": "gmail",
|
|
124
|
+
"success": True,
|
|
125
|
+
"action": "create",
|
|
126
|
+
"label_id": data.get("id"),
|
|
127
|
+
"label_name": data.get("name")
|
|
128
|
+
}, indent=2)
|
|
129
|
+
|
|
130
|
+
def _update_gmail_label(self, execute_tool) -> str:
|
|
131
|
+
"""Updates a Gmail label."""
|
|
132
|
+
if not self.label_id:
|
|
133
|
+
return "Error: label_id is required for update action"
|
|
134
|
+
|
|
135
|
+
arguments = {
|
|
136
|
+
"userId": "me",
|
|
137
|
+
"id": self.label_id
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if self.new_name:
|
|
141
|
+
arguments["name"] = self.new_name
|
|
142
|
+
|
|
143
|
+
if self.color:
|
|
144
|
+
arguments["color"] = {
|
|
145
|
+
"backgroundColor": self.color,
|
|
146
|
+
"textColor": "#ffffff"
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if not self.new_name and not self.color:
|
|
150
|
+
return "Error: new_name or color is required for update action"
|
|
151
|
+
|
|
152
|
+
result = execute_tool(
|
|
153
|
+
tool_name="GMAIL_PATCH_LABEL",
|
|
154
|
+
arguments=arguments,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if isinstance(result, dict) and result.get("error"):
|
|
158
|
+
return f"Error updating Gmail label: {result.get('error')}"
|
|
159
|
+
|
|
160
|
+
data = result.get("data", {})
|
|
161
|
+
|
|
162
|
+
return json.dumps({
|
|
163
|
+
"provider": "gmail",
|
|
164
|
+
"success": True,
|
|
165
|
+
"action": "update",
|
|
166
|
+
"label_id": data.get("id"),
|
|
167
|
+
"label_name": data.get("name")
|
|
168
|
+
}, indent=2)
|
|
169
|
+
|
|
170
|
+
def _delete_gmail_label(self, execute_tool) -> str:
|
|
171
|
+
"""Deletes a Gmail label."""
|
|
172
|
+
if not self.label_id:
|
|
173
|
+
return "Error: label_id is required for delete action"
|
|
174
|
+
|
|
175
|
+
result = execute_tool(
|
|
176
|
+
tool_name="GMAIL_DELETE_LABEL",
|
|
177
|
+
arguments={
|
|
178
|
+
"user_id": "me",
|
|
179
|
+
"label_id": self.label_id
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if isinstance(result, dict) and result.get("error"):
|
|
184
|
+
return f"Error deleting Gmail label: {result.get('error')}"
|
|
185
|
+
|
|
186
|
+
return json.dumps({
|
|
187
|
+
"provider": "gmail",
|
|
188
|
+
"success": True,
|
|
189
|
+
"action": "delete",
|
|
190
|
+
"label_id": self.label_id
|
|
191
|
+
}, indent=2)
|
|
192
|
+
|
|
193
|
+
def _manage_outlook_categories(self, execute_tool) -> str:
|
|
194
|
+
"""Manages Outlook categories."""
|
|
195
|
+
if self.action == "list":
|
|
196
|
+
return self._list_outlook_categories(execute_tool)
|
|
197
|
+
elif self.action == "create":
|
|
198
|
+
return self._create_outlook_category(execute_tool)
|
|
199
|
+
elif self.action == "update":
|
|
200
|
+
return "Error: Outlook categories cannot be renamed via API. Delete and recreate instead."
|
|
201
|
+
elif self.action == "delete":
|
|
202
|
+
return "Error: Outlook category deletion not available. Categories can only be managed in Outlook settings."
|
|
203
|
+
else:
|
|
204
|
+
return f"Unknown action: {self.action}"
|
|
205
|
+
|
|
206
|
+
def _list_outlook_categories(self, execute_tool) -> str:
|
|
207
|
+
"""Lists all Outlook categories."""
|
|
208
|
+
result = execute_tool(
|
|
209
|
+
tool_name="OUTLOOK_GET_MASTER_CATEGORIES",
|
|
210
|
+
arguments={"user_id": "me"},
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
if isinstance(result, dict) and result.get("error"):
|
|
214
|
+
return f"Error listing Outlook categories: {result.get('error')}"
|
|
215
|
+
|
|
216
|
+
categories = result.get("data", {}).get("value", [])
|
|
217
|
+
|
|
218
|
+
formatted_categories = []
|
|
219
|
+
for cat in categories:
|
|
220
|
+
formatted_categories.append({
|
|
221
|
+
"id": cat.get("id"),
|
|
222
|
+
"name": cat.get("displayName"),
|
|
223
|
+
"color": cat.get("color")
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
return json.dumps({
|
|
227
|
+
"provider": "outlook",
|
|
228
|
+
"count": len(formatted_categories),
|
|
229
|
+
"categories": formatted_categories
|
|
230
|
+
}, indent=2)
|
|
231
|
+
|
|
232
|
+
def _create_outlook_category(self, execute_tool) -> str:
|
|
233
|
+
"""Creates an Outlook category."""
|
|
234
|
+
if not self.label_name:
|
|
235
|
+
return "Error: label_name is required for create action"
|
|
236
|
+
|
|
237
|
+
arguments = {"displayName": self.label_name}
|
|
238
|
+
|
|
239
|
+
if self.color:
|
|
240
|
+
arguments["color"] = self.color
|
|
241
|
+
|
|
242
|
+
result = execute_tool(
|
|
243
|
+
tool_name="OUTLOOK_CREATE_MASTER_CATEGORY",
|
|
244
|
+
arguments=arguments,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if isinstance(result, dict) and result.get("error"):
|
|
248
|
+
return f"Error creating Outlook category: {result.get('error')}"
|
|
249
|
+
|
|
250
|
+
data = result.get("data", {})
|
|
251
|
+
|
|
252
|
+
return json.dumps({
|
|
253
|
+
"provider": "outlook",
|
|
254
|
+
"success": True,
|
|
255
|
+
"action": "create",
|
|
256
|
+
"category_id": data.get("id"),
|
|
257
|
+
"category_name": data.get("displayName"),
|
|
258
|
+
"color": data.get("color")
|
|
259
|
+
}, indent=2)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
if __name__ == "__main__":
|
|
263
|
+
import sys
|
|
264
|
+
import os
|
|
265
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
|
266
|
+
|
|
267
|
+
print("=" * 60)
|
|
268
|
+
print("ManageLabels Test Suite")
|
|
269
|
+
print("=" * 60)
|
|
270
|
+
print()
|
|
271
|
+
|
|
272
|
+
# Test 1: List Gmail labels
|
|
273
|
+
print("Test 1: List Gmail labels")
|
|
274
|
+
print("-" * 60)
|
|
275
|
+
tool = ManageLabels(provider="gmail", action="list")
|
|
276
|
+
result = tool.run()
|
|
277
|
+
# Just show first few labels
|
|
278
|
+
import json
|
|
279
|
+
data = json.loads(result)
|
|
280
|
+
data["labels"] = data["labels"][:5]
|
|
281
|
+
print(json.dumps(data, indent=2))
|
|
282
|
+
print()
|
|
283
|
+
|
|
284
|
+
# Test 2: List Outlook categories
|
|
285
|
+
print("Test 2: List Outlook categories")
|
|
286
|
+
print("-" * 60)
|
|
287
|
+
tool = ManageLabels(provider="outlook", action="list")
|
|
288
|
+
result = tool.run()
|
|
289
|
+
print(result)
|
|
290
|
+
print()
|
|
291
|
+
|
|
292
|
+
print("=" * 60)
|
|
293
|
+
print("Tests completed!")
|
|
294
|
+
print("=" * 60)
|
|
295
|
+
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
from typing import Literal, Optional
|
|
2
|
+
from agency_swarm.tools import BaseTool
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProductSearch(BaseTool):
|
|
10
|
+
"""
|
|
11
|
+
Searches for products on Google Shopping.
|
|
12
|
+
|
|
13
|
+
Returns product listings with prices, ratings, sellers, and availability.
|
|
14
|
+
Useful for price comparisons, finding deals, and product research.
|
|
15
|
+
|
|
16
|
+
RATE LIMIT: This tool can only be called ONCE per each user request (message) to save API costs.
|
|
17
|
+
Make sure to request enough results in a single call.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
query: str = Field(
|
|
21
|
+
...,
|
|
22
|
+
description="Search query for products (e.g., 'iPhone 15 Pro', 'running shoes', 'wireless headphones')"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
location: Optional[str] = Field(
|
|
26
|
+
default=None,
|
|
27
|
+
description="Location for the search (e.g., 'United States', 'New York', 'London'). Affects pricing and availability."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
country: Optional[str] = Field(
|
|
31
|
+
default="us",
|
|
32
|
+
description="Country code (e.g., 'us', 'gb', 'de', 'fr', 'jp')"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
language: Optional[str] = Field(
|
|
36
|
+
default="en",
|
|
37
|
+
description="Interface language (e.g., 'en', 'es', 'fr', 'de')"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
sort_by: Optional[Literal["relevance", "review_score", "price_low_to_high", "price_high_to_low"]] = Field(
|
|
41
|
+
default="relevance",
|
|
42
|
+
description="Sort results by: 'relevance', 'review_score', 'price_low_to_high', or 'price_high_to_low'"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
price_min: Optional[float] = Field(
|
|
46
|
+
default=None,
|
|
47
|
+
description="Minimum price filter"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
price_max: Optional[float] = Field(
|
|
51
|
+
default=None,
|
|
52
|
+
description="Maximum price filter"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
condition: Optional[Literal["new", "used"]] = Field(
|
|
56
|
+
default=None,
|
|
57
|
+
description="Filter by product condition: 'new' or 'used'"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
num_results: int = Field(
|
|
61
|
+
default=10,
|
|
62
|
+
ge=1,
|
|
63
|
+
le=60,
|
|
64
|
+
description="Number of results to return (1-60)"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
page: int = Field(
|
|
68
|
+
default=1,
|
|
69
|
+
ge=1,
|
|
70
|
+
description="Page number for pagination"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def run(self):
|
|
74
|
+
try:
|
|
75
|
+
import requests
|
|
76
|
+
|
|
77
|
+
# Rate limiting: Check if already called in this session
|
|
78
|
+
if self.context and self.context.get("product_search_called", False):
|
|
79
|
+
return "Error: ProductSearch can only be called once per user request to save API costs. Use the results from the previous search or web search tool."
|
|
80
|
+
|
|
81
|
+
api_key = os.getenv("SEARCH_API_KEY")
|
|
82
|
+
if not api_key:
|
|
83
|
+
raise ValueError("SEARCH_API_KEY is not set. Add it to your .env to use ProductSearch.")
|
|
84
|
+
|
|
85
|
+
# Build request parameters
|
|
86
|
+
params = {
|
|
87
|
+
"engine": "google_shopping",
|
|
88
|
+
"api_key": api_key,
|
|
89
|
+
"q": self.query,
|
|
90
|
+
"num": self.num_results,
|
|
91
|
+
"page": self.page
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Add optional parameters
|
|
95
|
+
if self.location:
|
|
96
|
+
params["location"] = self.location
|
|
97
|
+
|
|
98
|
+
if self.country:
|
|
99
|
+
params["gl"] = self.country
|
|
100
|
+
|
|
101
|
+
if self.language:
|
|
102
|
+
params["hl"] = self.language
|
|
103
|
+
|
|
104
|
+
if self.sort_by and self.sort_by != "relevance":
|
|
105
|
+
params["sort_by"] = self.sort_by
|
|
106
|
+
|
|
107
|
+
if self.price_min is not None:
|
|
108
|
+
params["price_min"] = str(self.price_min)
|
|
109
|
+
|
|
110
|
+
if self.price_max is not None:
|
|
111
|
+
params["price_max"] = str(self.price_max)
|
|
112
|
+
|
|
113
|
+
if self.condition:
|
|
114
|
+
params["condition"] = self.condition
|
|
115
|
+
|
|
116
|
+
# Make API request
|
|
117
|
+
response = requests.get(
|
|
118
|
+
"https://www.searchapi.io/api/v1/search",
|
|
119
|
+
params=params,
|
|
120
|
+
timeout=30
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
if response.status_code != 200:
|
|
124
|
+
return f"Error: API returned status {response.status_code}: {response.text}"
|
|
125
|
+
|
|
126
|
+
data = response.json()
|
|
127
|
+
|
|
128
|
+
# Check for API errors
|
|
129
|
+
if "error" in data:
|
|
130
|
+
return f"Error from API: {data['error']}"
|
|
131
|
+
|
|
132
|
+
# Extract and format results
|
|
133
|
+
shopping_results = data.get("shopping_results", [])
|
|
134
|
+
shopping_ads = data.get("shopping_ads", [])
|
|
135
|
+
|
|
136
|
+
# Combine results (ads first, then organic)
|
|
137
|
+
all_products = []
|
|
138
|
+
|
|
139
|
+
# Process shopping ads
|
|
140
|
+
for ad in shopping_ads[:5]: # Limit ads to 5
|
|
141
|
+
all_products.append({
|
|
142
|
+
"type": "sponsored",
|
|
143
|
+
"title": ad.get("title"),
|
|
144
|
+
"price": ad.get("price"),
|
|
145
|
+
"extracted_price": ad.get("extracted_price"),
|
|
146
|
+
"original_price": ad.get("original_price"),
|
|
147
|
+
"seller": ad.get("seller"),
|
|
148
|
+
"rating": ad.get("rating"),
|
|
149
|
+
"reviews": ad.get("reviews"),
|
|
150
|
+
"condition": ad.get("condition"),
|
|
151
|
+
"delivery": ad.get("delivery"),
|
|
152
|
+
"link": ad.get("link"),
|
|
153
|
+
"image": ad.get("image")
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
# Process organic shopping results
|
|
157
|
+
for result in shopping_results:
|
|
158
|
+
all_products.append({
|
|
159
|
+
"type": "organic",
|
|
160
|
+
"title": result.get("title"),
|
|
161
|
+
"price": result.get("price"),
|
|
162
|
+
"extracted_price": result.get("extracted_price"),
|
|
163
|
+
"original_price": result.get("original_price"),
|
|
164
|
+
"seller": result.get("seller"),
|
|
165
|
+
"rating": result.get("rating"),
|
|
166
|
+
"reviews": result.get("reviews"),
|
|
167
|
+
"condition": result.get("condition"),
|
|
168
|
+
"delivery": result.get("delivery"),
|
|
169
|
+
"offers": result.get("offers"),
|
|
170
|
+
"product_id": result.get("product_id") or result.get("prds"),
|
|
171
|
+
"product_link": result.get("product_link"),
|
|
172
|
+
"thumbnail": result.get("thumbnail")
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
# Mark as called in shared state (rate limiting)
|
|
176
|
+
if self.context:
|
|
177
|
+
self.context.set("product_search_called", True)
|
|
178
|
+
|
|
179
|
+
return json.dumps({
|
|
180
|
+
"query": self.query,
|
|
181
|
+
"location": self.location,
|
|
182
|
+
"sort_by": self.sort_by,
|
|
183
|
+
"filters": {
|
|
184
|
+
"price_min": self.price_min,
|
|
185
|
+
"price_max": self.price_max,
|
|
186
|
+
"condition": self.condition
|
|
187
|
+
},
|
|
188
|
+
"total_results": len(all_products),
|
|
189
|
+
"page": self.page,
|
|
190
|
+
"products": all_products
|
|
191
|
+
}, indent=2)
|
|
192
|
+
|
|
193
|
+
except Exception as e:
|
|
194
|
+
return f"Error searching products: {str(e)}"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
import sys
|
|
200
|
+
import os
|
|
201
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
|
202
|
+
|
|
203
|
+
print("=" * 60)
|
|
204
|
+
print("ProductSearch Test Suite")
|
|
205
|
+
print("=" * 60)
|
|
206
|
+
print()
|
|
207
|
+
|
|
208
|
+
# Test 1: Basic search
|
|
209
|
+
print("Test 1: Basic product search")
|
|
210
|
+
print("-" * 60)
|
|
211
|
+
tool = ProductSearch(
|
|
212
|
+
query="wireless headphones",
|
|
213
|
+
num_results=5
|
|
214
|
+
)
|
|
215
|
+
result = tool.run()
|
|
216
|
+
|
|
217
|
+
try:
|
|
218
|
+
data = json.loads(result)
|
|
219
|
+
print(f"Query: {data['query']}")
|
|
220
|
+
print(f"Total results: {data['total_results']}")
|
|
221
|
+
if data['products']:
|
|
222
|
+
print(f"First product: {data['products'][0]['title']}")
|
|
223
|
+
print(f" Price: {data['products'][0]['price']}")
|
|
224
|
+
print(f" Seller: {data['products'][0]['seller']}")
|
|
225
|
+
except json.JSONDecodeError:
|
|
226
|
+
print(result)
|
|
227
|
+
print()
|
|
228
|
+
|
|
229
|
+
# Test 2: Search with price filter
|
|
230
|
+
print("Test 2: Search with price filter")
|
|
231
|
+
print("-" * 60)
|
|
232
|
+
tool = ProductSearch(
|
|
233
|
+
query="running shoes",
|
|
234
|
+
price_min=50,
|
|
235
|
+
price_max=150,
|
|
236
|
+
sort_by="price_low_to_high",
|
|
237
|
+
num_results=3
|
|
238
|
+
)
|
|
239
|
+
result = tool.run()
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
data = json.loads(result)
|
|
243
|
+
print(f"Query: {data['query']}")
|
|
244
|
+
print(f"Filters: {data['filters']}")
|
|
245
|
+
print(f"Sort: {data['sort_by']}")
|
|
246
|
+
print(f"Results: {data['total_results']}")
|
|
247
|
+
except json.JSONDecodeError:
|
|
248
|
+
print(result)
|
|
249
|
+
print()
|
|
250
|
+
|
|
251
|
+
print("=" * 60)
|
|
252
|
+
print("Tests completed!")
|
|
253
|
+
print("=" * 60)
|
|
254
|
+
|