ai-parrot 0.17.2__cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
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.
- agentui/.prettierrc +15 -0
- agentui/QUICKSTART.md +272 -0
- agentui/README.md +59 -0
- agentui/env.example +16 -0
- agentui/jsconfig.json +14 -0
- agentui/package-lock.json +4242 -0
- agentui/package.json +34 -0
- agentui/scripts/postinstall/apply-patches.mjs +260 -0
- agentui/src/app.css +61 -0
- agentui/src/app.d.ts +13 -0
- agentui/src/app.html +12 -0
- agentui/src/components/LoadingSpinner.svelte +64 -0
- agentui/src/components/ThemeSwitcher.svelte +159 -0
- agentui/src/components/index.js +4 -0
- agentui/src/lib/api/bots.ts +60 -0
- agentui/src/lib/api/chat.ts +22 -0
- agentui/src/lib/api/http.ts +25 -0
- agentui/src/lib/components/BotCard.svelte +33 -0
- agentui/src/lib/components/ChatBubble.svelte +63 -0
- agentui/src/lib/components/Toast.svelte +21 -0
- agentui/src/lib/config.ts +20 -0
- agentui/src/lib/stores/auth.svelte.ts +73 -0
- agentui/src/lib/stores/theme.svelte.js +64 -0
- agentui/src/lib/stores/toast.svelte.ts +31 -0
- agentui/src/lib/utils/conversation.ts +39 -0
- agentui/src/routes/+layout.svelte +20 -0
- agentui/src/routes/+page.svelte +232 -0
- agentui/src/routes/login/+page.svelte +200 -0
- agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
- agentui/src/routes/talk/[agentId]/+page.ts +7 -0
- agentui/static/README.md +1 -0
- agentui/svelte.config.js +11 -0
- agentui/tailwind.config.ts +53 -0
- agentui/tsconfig.json +3 -0
- agentui/vite.config.ts +10 -0
- ai_parrot-0.17.2.dist-info/METADATA +472 -0
- ai_parrot-0.17.2.dist-info/RECORD +535 -0
- ai_parrot-0.17.2.dist-info/WHEEL +6 -0
- ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
- ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
- ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
- crew-builder/.prettierrc +15 -0
- crew-builder/QUICKSTART.md +259 -0
- crew-builder/README.md +113 -0
- crew-builder/env.example +17 -0
- crew-builder/jsconfig.json +14 -0
- crew-builder/package-lock.json +4182 -0
- crew-builder/package.json +37 -0
- crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
- crew-builder/src/app.css +62 -0
- crew-builder/src/app.d.ts +13 -0
- crew-builder/src/app.html +12 -0
- crew-builder/src/components/LoadingSpinner.svelte +64 -0
- crew-builder/src/components/ThemeSwitcher.svelte +149 -0
- crew-builder/src/components/index.js +9 -0
- crew-builder/src/lib/api/bots.ts +60 -0
- crew-builder/src/lib/api/chat.ts +80 -0
- crew-builder/src/lib/api/client.ts +56 -0
- crew-builder/src/lib/api/crew/crew.ts +136 -0
- crew-builder/src/lib/api/index.ts +5 -0
- crew-builder/src/lib/api/o365/auth.ts +65 -0
- crew-builder/src/lib/auth/auth.ts +54 -0
- crew-builder/src/lib/components/AgentNode.svelte +43 -0
- crew-builder/src/lib/components/BotCard.svelte +33 -0
- crew-builder/src/lib/components/ChatBubble.svelte +67 -0
- crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
- crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
- crew-builder/src/lib/components/JsonViewer.svelte +24 -0
- crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
- crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
- crew-builder/src/lib/components/Toast.svelte +67 -0
- crew-builder/src/lib/components/Toolbar.svelte +157 -0
- crew-builder/src/lib/components/index.ts +10 -0
- crew-builder/src/lib/config.ts +8 -0
- crew-builder/src/lib/stores/auth.svelte.ts +228 -0
- crew-builder/src/lib/stores/crewStore.ts +369 -0
- crew-builder/src/lib/stores/theme.svelte.js +145 -0
- crew-builder/src/lib/stores/toast.svelte.ts +69 -0
- crew-builder/src/lib/utils/conversation.ts +39 -0
- crew-builder/src/lib/utils/markdown.ts +122 -0
- crew-builder/src/lib/utils/talkHistory.ts +47 -0
- crew-builder/src/routes/+layout.svelte +20 -0
- crew-builder/src/routes/+page.svelte +539 -0
- crew-builder/src/routes/agents/+page.svelte +247 -0
- crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
- crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
- crew-builder/src/routes/builder/+page.svelte +204 -0
- crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
- crew-builder/src/routes/crew/ask/+page.ts +1 -0
- crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
- crew-builder/src/routes/login/+page.svelte +197 -0
- crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
- crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
- crew-builder/static/README.md +1 -0
- crew-builder/svelte.config.js +11 -0
- crew-builder/tailwind.config.ts +53 -0
- crew-builder/tsconfig.json +3 -0
- crew-builder/vite.config.ts +10 -0
- mcp_servers/calculator_server.py +309 -0
- parrot/__init__.py +27 -0
- parrot/__pycache__/__init__.cpython-310.pyc +0 -0
- parrot/__pycache__/version.cpython-310.pyc +0 -0
- parrot/_version.py +34 -0
- parrot/a2a/__init__.py +48 -0
- parrot/a2a/client.py +658 -0
- parrot/a2a/discovery.py +89 -0
- parrot/a2a/mixin.py +257 -0
- parrot/a2a/models.py +376 -0
- parrot/a2a/server.py +770 -0
- parrot/agents/__init__.py +29 -0
- parrot/bots/__init__.py +12 -0
- parrot/bots/a2a_agent.py +19 -0
- parrot/bots/abstract.py +3139 -0
- parrot/bots/agent.py +1129 -0
- parrot/bots/basic.py +9 -0
- parrot/bots/chatbot.py +669 -0
- parrot/bots/data.py +1618 -0
- parrot/bots/database/__init__.py +5 -0
- parrot/bots/database/abstract.py +3071 -0
- parrot/bots/database/cache.py +286 -0
- parrot/bots/database/models.py +468 -0
- parrot/bots/database/prompts.py +154 -0
- parrot/bots/database/retries.py +98 -0
- parrot/bots/database/router.py +269 -0
- parrot/bots/database/sql.py +41 -0
- parrot/bots/db/__init__.py +6 -0
- parrot/bots/db/abstract.py +556 -0
- parrot/bots/db/bigquery.py +602 -0
- parrot/bots/db/cache.py +85 -0
- parrot/bots/db/documentdb.py +668 -0
- parrot/bots/db/elastic.py +1014 -0
- parrot/bots/db/influx.py +898 -0
- parrot/bots/db/mock.py +96 -0
- parrot/bots/db/multi.py +783 -0
- parrot/bots/db/prompts.py +185 -0
- parrot/bots/db/sql.py +1255 -0
- parrot/bots/db/tools.py +212 -0
- parrot/bots/document.py +680 -0
- parrot/bots/hrbot.py +15 -0
- parrot/bots/kb.py +170 -0
- parrot/bots/mcp.py +36 -0
- parrot/bots/orchestration/README.md +463 -0
- parrot/bots/orchestration/__init__.py +1 -0
- parrot/bots/orchestration/agent.py +155 -0
- parrot/bots/orchestration/crew.py +3330 -0
- parrot/bots/orchestration/fsm.py +1179 -0
- parrot/bots/orchestration/hr.py +434 -0
- parrot/bots/orchestration/storage/__init__.py +4 -0
- parrot/bots/orchestration/storage/memory.py +100 -0
- parrot/bots/orchestration/storage/mixin.py +119 -0
- parrot/bots/orchestration/verify.py +202 -0
- parrot/bots/product.py +204 -0
- parrot/bots/prompts/__init__.py +96 -0
- parrot/bots/prompts/agents.py +155 -0
- parrot/bots/prompts/data.py +216 -0
- parrot/bots/prompts/output_generation.py +8 -0
- parrot/bots/scraper/__init__.py +3 -0
- parrot/bots/scraper/models.py +122 -0
- parrot/bots/scraper/scraper.py +1173 -0
- parrot/bots/scraper/templates.py +115 -0
- parrot/bots/stores/__init__.py +5 -0
- parrot/bots/stores/local.py +172 -0
- parrot/bots/webdev.py +81 -0
- parrot/cli.py +17 -0
- parrot/clients/__init__.py +16 -0
- parrot/clients/base.py +1491 -0
- parrot/clients/claude.py +1191 -0
- parrot/clients/factory.py +129 -0
- parrot/clients/google.py +4567 -0
- parrot/clients/gpt.py +1975 -0
- parrot/clients/grok.py +432 -0
- parrot/clients/groq.py +986 -0
- parrot/clients/hf.py +582 -0
- parrot/clients/models.py +18 -0
- parrot/conf.py +395 -0
- parrot/embeddings/__init__.py +9 -0
- parrot/embeddings/base.py +157 -0
- parrot/embeddings/google.py +98 -0
- parrot/embeddings/huggingface.py +74 -0
- parrot/embeddings/openai.py +84 -0
- parrot/embeddings/processor.py +88 -0
- parrot/exceptions.c +13868 -0
- parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/exceptions.pxd +22 -0
- parrot/exceptions.pxi +15 -0
- parrot/exceptions.pyx +44 -0
- parrot/generators/__init__.py +29 -0
- parrot/generators/base.py +200 -0
- parrot/generators/html.py +293 -0
- parrot/generators/react.py +205 -0
- parrot/generators/streamlit.py +203 -0
- parrot/generators/template.py +105 -0
- parrot/handlers/__init__.py +4 -0
- parrot/handlers/agent.py +861 -0
- parrot/handlers/agents/__init__.py +1 -0
- parrot/handlers/agents/abstract.py +900 -0
- parrot/handlers/bots.py +338 -0
- parrot/handlers/chat.py +915 -0
- parrot/handlers/creation.sql +192 -0
- parrot/handlers/crew/ARCHITECTURE.md +362 -0
- parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
- parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
- parrot/handlers/crew/__init__.py +0 -0
- parrot/handlers/crew/handler.py +801 -0
- parrot/handlers/crew/models.py +229 -0
- parrot/handlers/crew/redis_persistence.py +523 -0
- parrot/handlers/jobs/__init__.py +10 -0
- parrot/handlers/jobs/job.py +384 -0
- parrot/handlers/jobs/mixin.py +627 -0
- parrot/handlers/jobs/models.py +115 -0
- parrot/handlers/jobs/worker.py +31 -0
- parrot/handlers/models.py +596 -0
- parrot/handlers/o365_auth.py +105 -0
- parrot/handlers/stream.py +337 -0
- parrot/interfaces/__init__.py +6 -0
- parrot/interfaces/aws.py +143 -0
- parrot/interfaces/credentials.py +113 -0
- parrot/interfaces/database.py +27 -0
- parrot/interfaces/google.py +1123 -0
- parrot/interfaces/hierarchy.py +1227 -0
- parrot/interfaces/http.py +651 -0
- parrot/interfaces/images/__init__.py +0 -0
- parrot/interfaces/images/plugins/__init__.py +24 -0
- parrot/interfaces/images/plugins/abstract.py +58 -0
- parrot/interfaces/images/plugins/analisys.py +148 -0
- parrot/interfaces/images/plugins/classify.py +150 -0
- parrot/interfaces/images/plugins/classifybase.py +182 -0
- parrot/interfaces/images/plugins/detect.py +150 -0
- parrot/interfaces/images/plugins/exif.py +1103 -0
- parrot/interfaces/images/plugins/hash.py +52 -0
- parrot/interfaces/images/plugins/vision.py +104 -0
- parrot/interfaces/images/plugins/yolo.py +66 -0
- parrot/interfaces/images/plugins/zerodetect.py +197 -0
- parrot/interfaces/o365.py +978 -0
- parrot/interfaces/onedrive.py +822 -0
- parrot/interfaces/sharepoint.py +1435 -0
- parrot/interfaces/soap.py +257 -0
- parrot/loaders/__init__.py +8 -0
- parrot/loaders/abstract.py +1131 -0
- parrot/loaders/audio.py +199 -0
- parrot/loaders/basepdf.py +53 -0
- parrot/loaders/basevideo.py +1568 -0
- parrot/loaders/csv.py +409 -0
- parrot/loaders/docx.py +116 -0
- parrot/loaders/epubloader.py +316 -0
- parrot/loaders/excel.py +199 -0
- parrot/loaders/factory.py +55 -0
- parrot/loaders/files/__init__.py +0 -0
- parrot/loaders/files/abstract.py +39 -0
- parrot/loaders/files/html.py +26 -0
- parrot/loaders/files/text.py +63 -0
- parrot/loaders/html.py +152 -0
- parrot/loaders/markdown.py +442 -0
- parrot/loaders/pdf.py +373 -0
- parrot/loaders/pdfmark.py +320 -0
- parrot/loaders/pdftables.py +506 -0
- parrot/loaders/ppt.py +476 -0
- parrot/loaders/qa.py +63 -0
- parrot/loaders/splitters/__init__.py +10 -0
- parrot/loaders/splitters/base.py +138 -0
- parrot/loaders/splitters/md.py +228 -0
- parrot/loaders/splitters/token.py +143 -0
- parrot/loaders/txt.py +26 -0
- parrot/loaders/video.py +89 -0
- parrot/loaders/videolocal.py +218 -0
- parrot/loaders/videounderstanding.py +377 -0
- parrot/loaders/vimeo.py +167 -0
- parrot/loaders/web.py +599 -0
- parrot/loaders/youtube.py +504 -0
- parrot/manager/__init__.py +5 -0
- parrot/manager/manager.py +1030 -0
- parrot/mcp/__init__.py +28 -0
- parrot/mcp/adapter.py +105 -0
- parrot/mcp/cli.py +174 -0
- parrot/mcp/client.py +119 -0
- parrot/mcp/config.py +75 -0
- parrot/mcp/integration.py +842 -0
- parrot/mcp/oauth.py +933 -0
- parrot/mcp/server.py +225 -0
- parrot/mcp/transports/__init__.py +3 -0
- parrot/mcp/transports/base.py +279 -0
- parrot/mcp/transports/grpc_session.py +163 -0
- parrot/mcp/transports/http.py +312 -0
- parrot/mcp/transports/mcp.proto +108 -0
- parrot/mcp/transports/quic.py +1082 -0
- parrot/mcp/transports/sse.py +330 -0
- parrot/mcp/transports/stdio.py +309 -0
- parrot/mcp/transports/unix.py +395 -0
- parrot/mcp/transports/websocket.py +547 -0
- parrot/memory/__init__.py +16 -0
- parrot/memory/abstract.py +209 -0
- parrot/memory/agent.py +32 -0
- parrot/memory/cache.py +175 -0
- parrot/memory/core.py +555 -0
- parrot/memory/file.py +153 -0
- parrot/memory/mem.py +131 -0
- parrot/memory/redis.py +613 -0
- parrot/models/__init__.py +46 -0
- parrot/models/basic.py +118 -0
- parrot/models/compliance.py +208 -0
- parrot/models/crew.py +395 -0
- parrot/models/detections.py +654 -0
- parrot/models/generation.py +85 -0
- parrot/models/google.py +223 -0
- parrot/models/groq.py +23 -0
- parrot/models/openai.py +30 -0
- parrot/models/outputs.py +285 -0
- parrot/models/responses.py +938 -0
- parrot/notifications/__init__.py +743 -0
- parrot/openapi/__init__.py +3 -0
- parrot/openapi/components.yaml +641 -0
- parrot/openapi/config.py +322 -0
- parrot/outputs/__init__.py +32 -0
- parrot/outputs/formats/__init__.py +108 -0
- parrot/outputs/formats/altair.py +359 -0
- parrot/outputs/formats/application.py +122 -0
- parrot/outputs/formats/base.py +351 -0
- parrot/outputs/formats/bokeh.py +356 -0
- parrot/outputs/formats/card.py +424 -0
- parrot/outputs/formats/chart.py +436 -0
- parrot/outputs/formats/d3.py +255 -0
- parrot/outputs/formats/echarts.py +310 -0
- parrot/outputs/formats/generators/__init__.py +0 -0
- parrot/outputs/formats/generators/abstract.py +61 -0
- parrot/outputs/formats/generators/panel.py +145 -0
- parrot/outputs/formats/generators/streamlit.py +86 -0
- parrot/outputs/formats/generators/terminal.py +63 -0
- parrot/outputs/formats/holoviews.py +310 -0
- parrot/outputs/formats/html.py +147 -0
- parrot/outputs/formats/jinja2.py +46 -0
- parrot/outputs/formats/json.py +87 -0
- parrot/outputs/formats/map.py +933 -0
- parrot/outputs/formats/markdown.py +172 -0
- parrot/outputs/formats/matplotlib.py +237 -0
- parrot/outputs/formats/mixins/__init__.py +0 -0
- parrot/outputs/formats/mixins/emaps.py +855 -0
- parrot/outputs/formats/plotly.py +341 -0
- parrot/outputs/formats/seaborn.py +310 -0
- parrot/outputs/formats/table.py +397 -0
- parrot/outputs/formats/template_report.py +138 -0
- parrot/outputs/formats/yaml.py +125 -0
- parrot/outputs/formatter.py +152 -0
- parrot/outputs/templates/__init__.py +95 -0
- parrot/pipelines/__init__.py +0 -0
- parrot/pipelines/abstract.py +210 -0
- parrot/pipelines/detector.py +124 -0
- parrot/pipelines/models.py +90 -0
- parrot/pipelines/planogram.py +3002 -0
- parrot/pipelines/table.sql +97 -0
- parrot/plugins/__init__.py +106 -0
- parrot/plugins/importer.py +80 -0
- parrot/py.typed +0 -0
- parrot/registry/__init__.py +18 -0
- parrot/registry/registry.py +594 -0
- parrot/scheduler/__init__.py +1189 -0
- parrot/scheduler/models.py +60 -0
- parrot/security/__init__.py +16 -0
- parrot/security/prompt_injection.py +268 -0
- parrot/security/security_events.sql +25 -0
- parrot/services/__init__.py +1 -0
- parrot/services/mcp/__init__.py +8 -0
- parrot/services/mcp/config.py +13 -0
- parrot/services/mcp/server.py +295 -0
- parrot/services/o365_remote_auth.py +235 -0
- parrot/stores/__init__.py +7 -0
- parrot/stores/abstract.py +352 -0
- parrot/stores/arango.py +1090 -0
- parrot/stores/bigquery.py +1377 -0
- parrot/stores/cache.py +106 -0
- parrot/stores/empty.py +10 -0
- parrot/stores/faiss_store.py +1157 -0
- parrot/stores/kb/__init__.py +9 -0
- parrot/stores/kb/abstract.py +68 -0
- parrot/stores/kb/cache.py +165 -0
- parrot/stores/kb/doc.py +325 -0
- parrot/stores/kb/hierarchy.py +346 -0
- parrot/stores/kb/local.py +457 -0
- parrot/stores/kb/prompt.py +28 -0
- parrot/stores/kb/redis.py +659 -0
- parrot/stores/kb/store.py +115 -0
- parrot/stores/kb/user.py +374 -0
- parrot/stores/models.py +59 -0
- parrot/stores/pgvector.py +3 -0
- parrot/stores/postgres.py +2853 -0
- parrot/stores/utils/__init__.py +0 -0
- parrot/stores/utils/chunking.py +197 -0
- parrot/telemetry/__init__.py +3 -0
- parrot/telemetry/mixin.py +111 -0
- parrot/template/__init__.py +3 -0
- parrot/template/engine.py +259 -0
- parrot/tools/__init__.py +23 -0
- parrot/tools/abstract.py +644 -0
- parrot/tools/agent.py +363 -0
- parrot/tools/arangodbsearch.py +537 -0
- parrot/tools/arxiv_tool.py +188 -0
- parrot/tools/calculator/__init__.py +3 -0
- parrot/tools/calculator/operations/__init__.py +38 -0
- parrot/tools/calculator/operations/calculus.py +80 -0
- parrot/tools/calculator/operations/statistics.py +76 -0
- parrot/tools/calculator/tool.py +150 -0
- parrot/tools/cloudwatch.py +988 -0
- parrot/tools/codeinterpreter/__init__.py +127 -0
- parrot/tools/codeinterpreter/executor.py +371 -0
- parrot/tools/codeinterpreter/internals.py +473 -0
- parrot/tools/codeinterpreter/models.py +643 -0
- parrot/tools/codeinterpreter/prompts.py +224 -0
- parrot/tools/codeinterpreter/tool.py +664 -0
- parrot/tools/company_info/__init__.py +6 -0
- parrot/tools/company_info/tool.py +1138 -0
- parrot/tools/correlationanalysis.py +437 -0
- parrot/tools/database/abstract.py +286 -0
- parrot/tools/database/bq.py +115 -0
- parrot/tools/database/cache.py +284 -0
- parrot/tools/database/models.py +95 -0
- parrot/tools/database/pg.py +343 -0
- parrot/tools/databasequery.py +1159 -0
- parrot/tools/db.py +1800 -0
- parrot/tools/ddgo.py +370 -0
- parrot/tools/decorators.py +271 -0
- parrot/tools/dftohtml.py +282 -0
- parrot/tools/document.py +549 -0
- parrot/tools/ecs.py +819 -0
- parrot/tools/edareport.py +368 -0
- parrot/tools/elasticsearch.py +1049 -0
- parrot/tools/employees.py +462 -0
- parrot/tools/epson/__init__.py +96 -0
- parrot/tools/excel.py +683 -0
- parrot/tools/file/__init__.py +13 -0
- parrot/tools/file/abstract.py +76 -0
- parrot/tools/file/gcs.py +378 -0
- parrot/tools/file/local.py +284 -0
- parrot/tools/file/s3.py +511 -0
- parrot/tools/file/tmp.py +309 -0
- parrot/tools/file/tool.py +501 -0
- parrot/tools/file_reader.py +129 -0
- parrot/tools/flowtask/__init__.py +19 -0
- parrot/tools/flowtask/tool.py +761 -0
- parrot/tools/gittoolkit.py +508 -0
- parrot/tools/google/__init__.py +18 -0
- parrot/tools/google/base.py +169 -0
- parrot/tools/google/tools.py +1251 -0
- parrot/tools/googlelocation.py +5 -0
- parrot/tools/googleroutes.py +5 -0
- parrot/tools/googlesearch.py +5 -0
- parrot/tools/googlesitesearch.py +5 -0
- parrot/tools/googlevoice.py +2 -0
- parrot/tools/gvoice.py +695 -0
- parrot/tools/ibisworld/README.md +225 -0
- parrot/tools/ibisworld/__init__.py +11 -0
- parrot/tools/ibisworld/tool.py +366 -0
- parrot/tools/jiratoolkit.py +1718 -0
- parrot/tools/manager.py +1098 -0
- parrot/tools/math.py +152 -0
- parrot/tools/metadata.py +476 -0
- parrot/tools/msteams.py +1621 -0
- parrot/tools/msword.py +635 -0
- parrot/tools/multidb.py +580 -0
- parrot/tools/multistoresearch.py +369 -0
- parrot/tools/networkninja.py +167 -0
- parrot/tools/nextstop/__init__.py +4 -0
- parrot/tools/nextstop/base.py +286 -0
- parrot/tools/nextstop/employee.py +733 -0
- parrot/tools/nextstop/store.py +462 -0
- parrot/tools/notification.py +435 -0
- parrot/tools/o365/__init__.py +42 -0
- parrot/tools/o365/base.py +295 -0
- parrot/tools/o365/bundle.py +522 -0
- parrot/tools/o365/events.py +554 -0
- parrot/tools/o365/mail.py +992 -0
- parrot/tools/o365/onedrive.py +497 -0
- parrot/tools/o365/sharepoint.py +641 -0
- parrot/tools/openapi_toolkit.py +904 -0
- parrot/tools/openweather.py +527 -0
- parrot/tools/pdfprint.py +1001 -0
- parrot/tools/powerbi.py +518 -0
- parrot/tools/powerpoint.py +1113 -0
- parrot/tools/pricestool.py +146 -0
- parrot/tools/products/__init__.py +246 -0
- parrot/tools/prophet_tool.py +171 -0
- parrot/tools/pythonpandas.py +630 -0
- parrot/tools/pythonrepl.py +910 -0
- parrot/tools/qsource.py +436 -0
- parrot/tools/querytoolkit.py +395 -0
- parrot/tools/quickeda.py +827 -0
- parrot/tools/resttool.py +553 -0
- parrot/tools/retail/__init__.py +0 -0
- parrot/tools/retail/bby.py +528 -0
- parrot/tools/sandboxtool.py +703 -0
- parrot/tools/sassie/__init__.py +352 -0
- parrot/tools/scraping/__init__.py +7 -0
- parrot/tools/scraping/docs/select.md +466 -0
- parrot/tools/scraping/documentation.md +1278 -0
- parrot/tools/scraping/driver.py +436 -0
- parrot/tools/scraping/models.py +576 -0
- parrot/tools/scraping/options.py +85 -0
- parrot/tools/scraping/orchestrator.py +517 -0
- parrot/tools/scraping/readme.md +740 -0
- parrot/tools/scraping/tool.py +3115 -0
- parrot/tools/seasonaldetection.py +642 -0
- parrot/tools/shell_tool/__init__.py +5 -0
- parrot/tools/shell_tool/actions.py +408 -0
- parrot/tools/shell_tool/engine.py +155 -0
- parrot/tools/shell_tool/models.py +322 -0
- parrot/tools/shell_tool/tool.py +442 -0
- parrot/tools/site_search.py +214 -0
- parrot/tools/textfile.py +418 -0
- parrot/tools/think.py +378 -0
- parrot/tools/toolkit.py +298 -0
- parrot/tools/webapp_tool.py +187 -0
- parrot/tools/whatif.py +1279 -0
- parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
- parrot/tools/workday/__init__.py +6 -0
- parrot/tools/workday/models.py +1389 -0
- parrot/tools/workday/tool.py +1293 -0
- parrot/tools/yfinance_tool.py +306 -0
- parrot/tools/zipcode.py +217 -0
- parrot/utils/__init__.py +2 -0
- parrot/utils/helpers.py +73 -0
- parrot/utils/parsers/__init__.py +5 -0
- parrot/utils/parsers/toml.c +12078 -0
- parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/parsers/toml.pyx +21 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpp +20936 -0
- parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/types.pyx +213 -0
- parrot/utils/uv.py +11 -0
- parrot/version.py +10 -0
- parrot/yaml-rs/Cargo.lock +350 -0
- parrot/yaml-rs/Cargo.toml +19 -0
- parrot/yaml-rs/pyproject.toml +19 -0
- parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
- parrot/yaml-rs/src/lib.rs +222 -0
- requirements/docker-compose.yml +24 -0
- requirements/requirements-dev.txt +21 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"""
|
|
2
|
+
BestBuy API Toolkit - Unified toolkit for BestBuy operations.
|
|
3
|
+
|
|
4
|
+
Provides methods for:
|
|
5
|
+
- Product search and information
|
|
6
|
+
- Store availability checking
|
|
7
|
+
- Inventory lookup
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import random
|
|
11
|
+
from typing import Dict, Any, Optional
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
from navconfig import config
|
|
14
|
+
from ...interfaces.http import HTTPService, UA_LIST
|
|
15
|
+
from ..toolkit import AbstractToolkit
|
|
16
|
+
from ..decorators import tool_schema
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ============================================================================
|
|
20
|
+
# Configuration
|
|
21
|
+
# ============================================================================
|
|
22
|
+
|
|
23
|
+
BESTBUY_API_KEY = config.get('BESTBUY_APIKEY')
|
|
24
|
+
|
|
25
|
+
# BestBuy cookies and headers for web scraping
|
|
26
|
+
CTT_LIST = [
|
|
27
|
+
"f3dbf688e45146555bb2b8604a993601",
|
|
28
|
+
"06f4dfe367e87866397ef32302f5042e",
|
|
29
|
+
"4e07e03ff03f5debc4e09ac4db9239ac"
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
SID_LIST = [
|
|
33
|
+
"d4fa1142-2998-4b68-af78-46d821bb3e1f",
|
|
34
|
+
"9627390e-b423-459f-83ee-7964dd05c9a8"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# ============================================================================
|
|
39
|
+
# Input Schemas
|
|
40
|
+
# ============================================================================
|
|
41
|
+
|
|
42
|
+
class ProductSearchInput(BaseModel):
|
|
43
|
+
"""Input schema for product search."""
|
|
44
|
+
search_terms: Optional[str] = Field(
|
|
45
|
+
default=None,
|
|
46
|
+
description="Search terms separated by commas (e.g., 'oven,stainless,steel')"
|
|
47
|
+
)
|
|
48
|
+
product_name: Optional[str] = Field(
|
|
49
|
+
default=None,
|
|
50
|
+
description="Specific product name to search for"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ProductAvailabilityInput(BaseModel):
|
|
55
|
+
"""Input schema for checking product availability."""
|
|
56
|
+
zipcode: str = Field(
|
|
57
|
+
description="ZIP code to check availability in"
|
|
58
|
+
)
|
|
59
|
+
sku: str = Field(
|
|
60
|
+
description="Product SKU to check"
|
|
61
|
+
)
|
|
62
|
+
location_id: str = Field(
|
|
63
|
+
description="Store location ID to check"
|
|
64
|
+
)
|
|
65
|
+
show_only_in_stock: bool = Field(
|
|
66
|
+
default=False,
|
|
67
|
+
description="Whether to only show stores with product in stock"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class StoreLocatorInput(BaseModel):
|
|
72
|
+
"""Input schema for finding stores."""
|
|
73
|
+
zipcode: str = Field(
|
|
74
|
+
description="ZIP code to search near"
|
|
75
|
+
)
|
|
76
|
+
radius: int = Field(
|
|
77
|
+
default=25,
|
|
78
|
+
description="Search radius in miles"
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ============================================================================
|
|
83
|
+
# BestBuy Toolkit
|
|
84
|
+
# ============================================================================
|
|
85
|
+
|
|
86
|
+
class BestBuyToolkit(AbstractToolkit):
|
|
87
|
+
"""
|
|
88
|
+
Toolkit for interacting with BestBuy API and services.
|
|
89
|
+
|
|
90
|
+
Provides methods for:
|
|
91
|
+
- Searching for products
|
|
92
|
+
- Getting product information
|
|
93
|
+
- Checking store availability
|
|
94
|
+
- Finding nearby stores
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
def __init__(
|
|
98
|
+
self,
|
|
99
|
+
api_key: Optional[str] = None,
|
|
100
|
+
use_proxy: bool = True,
|
|
101
|
+
**kwargs
|
|
102
|
+
):
|
|
103
|
+
"""
|
|
104
|
+
Initialize the BestBuy toolkit.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
api_key: BestBuy API key. If None, uses config.get('BESTBUY_APIKEY')
|
|
108
|
+
use_proxy: Whether to use proxy for requests
|
|
109
|
+
**kwargs: Additional toolkit configuration
|
|
110
|
+
"""
|
|
111
|
+
super().__init__(**kwargs)
|
|
112
|
+
|
|
113
|
+
self.api_key = api_key or BESTBUY_API_KEY
|
|
114
|
+
if not self.api_key:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
"BestBuy API key is required. "
|
|
117
|
+
"Set BESTBUY_APIKEY in config or pass api_key parameter."
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Initialize HTTPService for BestBuy website (availability checks)
|
|
121
|
+
self.http_web = HTTPService(
|
|
122
|
+
use_proxy=use_proxy,
|
|
123
|
+
cookies={
|
|
124
|
+
"CTT": random.choice(CTT_LIST),
|
|
125
|
+
"SID": random.choice(SID_LIST),
|
|
126
|
+
"bby_rdp": "l",
|
|
127
|
+
"bm_sz": "9F5ED0110AF18594E2347A89BB4AB998~YAAQxm1lX6EqYHGSAQAAw+apmhkhXIeGYEc4KnzUMsjeac3xEoQmTNz5+of62i3RXQL6fUI+0FvCb/jgSjiVQOcfaSF+LdLkOXP1F4urgeIcqp/dBAhu5MvZXaCQsT06bwr7j21ozhFfTTWhjz1HmZN8wecsE6WGbK6wXp/33ODKlLaGWkTutqHbkzvMiiHXBCs9hT8jVny0REfita4AfqTK85Y6/M6Uq4IaDLPBLnTtJ0cTlPHk1HmkG5EsnI46llghcx1KZnCGnvZfHdb2ME9YZJ2GmC2b7dNmAgyL/gSVpoNdCJOj5Jk6z/MCVhZ81OZfX4S01E2F1mBGq4uV5/1oK2KR4YgZP4dsTN8izEEPybUKGY3CyM1gOUc=~3556420~4277810",
|
|
128
|
+
"bby_cbc_lb": "p-browse-e",
|
|
129
|
+
"intl_splash": "false"
|
|
130
|
+
},
|
|
131
|
+
headers={
|
|
132
|
+
"Host": "www.bestbuy.com",
|
|
133
|
+
"Referer": "https://www.bestbuy.com/",
|
|
134
|
+
"TE": "trailers",
|
|
135
|
+
"Accept-Language": "en-US,en;q=0.5",
|
|
136
|
+
},
|
|
137
|
+
accept='application/json',
|
|
138
|
+
timeout=30
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Initialize HTTPService for BestBuy API (product search)
|
|
142
|
+
self.http_api = HTTPService(
|
|
143
|
+
use_proxy=True,
|
|
144
|
+
accept='application/json',
|
|
145
|
+
timeout=30
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
@tool_schema(ProductSearchInput)
|
|
149
|
+
async def search_products(
|
|
150
|
+
self,
|
|
151
|
+
search_terms: Optional[str] = None,
|
|
152
|
+
product_name: Optional[str] = None
|
|
153
|
+
) -> Dict[str, Any]:
|
|
154
|
+
"""
|
|
155
|
+
Search for products on BestBuy using product names or search terms.
|
|
156
|
+
|
|
157
|
+
Returns detailed product information including:
|
|
158
|
+
- SKU (needed for availability checks)
|
|
159
|
+
- Product name
|
|
160
|
+
- Sale price
|
|
161
|
+
- Customer reviews and ratings
|
|
162
|
+
- Manufacturer and model number
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
search_terms: Comma-separated search terms (e.g., "oven,stainless,steel")
|
|
166
|
+
product_name: Specific product name to search for
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Dictionary with list of matching products or error message
|
|
170
|
+
"""
|
|
171
|
+
# Build query string
|
|
172
|
+
if search_terms:
|
|
173
|
+
# Parse comma-separated terms
|
|
174
|
+
terms = [term.strip() for term in search_terms.split(',')]
|
|
175
|
+
query = '&'.join([f"search={term}" for term in terms])
|
|
176
|
+
elif product_name:
|
|
177
|
+
# Handle product name (can be comma-separated too)
|
|
178
|
+
if ',' in product_name:
|
|
179
|
+
terms = [term.strip() for term in product_name.split(',')]
|
|
180
|
+
query = '&'.join([f"search={term}" for term in terms])
|
|
181
|
+
else:
|
|
182
|
+
query = f"name={product_name.strip()}"
|
|
183
|
+
else:
|
|
184
|
+
return {
|
|
185
|
+
"error": "Either search_terms or product_name must be provided"
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# Build API URL
|
|
189
|
+
url = (
|
|
190
|
+
f"https://api.bestbuy.com/v1/products({query})"
|
|
191
|
+
f"?format=json"
|
|
192
|
+
f"&show=sku,name,salePrice,customerReviewAverage,customerReviewCount,manufacturer,modelNumber"
|
|
193
|
+
f"&apiKey={self.api_key}"
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
self.logger.debug(f"Searching BestBuy API: {url}")
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
# Make request using HTTPService
|
|
200
|
+
result, error = await self.http_api.request(
|
|
201
|
+
url=url,
|
|
202
|
+
method="GET",
|
|
203
|
+
client='httpx',
|
|
204
|
+
use_ssl=True,
|
|
205
|
+
follow_redirects=True
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if error:
|
|
209
|
+
self.logger.error(f"Error searching products: {error}")
|
|
210
|
+
return {"error": str(error)}
|
|
211
|
+
|
|
212
|
+
# Extract products
|
|
213
|
+
products = result.get('products', [])
|
|
214
|
+
|
|
215
|
+
if not products:
|
|
216
|
+
return {
|
|
217
|
+
"message": "No products found",
|
|
218
|
+
"products": []
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
"total": len(products),
|
|
223
|
+
"products": products
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
self.logger.error(f"Failed to search products: {e}")
|
|
228
|
+
return {"error": str(e)}
|
|
229
|
+
|
|
230
|
+
@tool_schema(ProductAvailabilityInput)
|
|
231
|
+
async def check_availability(
|
|
232
|
+
self,
|
|
233
|
+
zipcode: str,
|
|
234
|
+
sku: str,
|
|
235
|
+
location_id: str,
|
|
236
|
+
show_only_in_stock: bool = False
|
|
237
|
+
) -> Dict[str, Any]:
|
|
238
|
+
"""
|
|
239
|
+
Check product availability at a specific BestBuy store.
|
|
240
|
+
|
|
241
|
+
Returns detailed availability information including:
|
|
242
|
+
- Store information (name, address, hours)
|
|
243
|
+
- Product in-stock status
|
|
244
|
+
- Pickup eligibility
|
|
245
|
+
- On-shelf display status
|
|
246
|
+
- Available quantity
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
zipcode: ZIP code to check availability in
|
|
250
|
+
sku: Product SKU to check
|
|
251
|
+
location_id: Store location ID
|
|
252
|
+
show_only_in_stock: Whether to only show in-stock items
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
Dictionary with availability information or error message
|
|
256
|
+
"""
|
|
257
|
+
# Validate inputs
|
|
258
|
+
if not zipcode:
|
|
259
|
+
return {"error": "ZIP code is required"}
|
|
260
|
+
if not sku:
|
|
261
|
+
return {"error": "Product SKU is required"}
|
|
262
|
+
if not location_id:
|
|
263
|
+
return {"error": "Store location ID is required"}
|
|
264
|
+
|
|
265
|
+
# Build request payload
|
|
266
|
+
payload = {
|
|
267
|
+
"locationId": location_id,
|
|
268
|
+
"zipCode": zipcode,
|
|
269
|
+
"showOnShelf": True,
|
|
270
|
+
"lookupInStoreQuantity": True,
|
|
271
|
+
"xboxAllAccess": False,
|
|
272
|
+
"consolidated": True,
|
|
273
|
+
"showOnlyOnShelf": False,
|
|
274
|
+
"showInStore": True,
|
|
275
|
+
"pickupTypes": [
|
|
276
|
+
"UPS_ACCESS_POINT",
|
|
277
|
+
"FEDEX_HAL"
|
|
278
|
+
],
|
|
279
|
+
"onlyBestBuyLocations": True,
|
|
280
|
+
"items": [
|
|
281
|
+
{
|
|
282
|
+
"sku": sku,
|
|
283
|
+
"condition": None,
|
|
284
|
+
"quantity": 1,
|
|
285
|
+
"itemSeqNumber": "1",
|
|
286
|
+
"reservationToken": None,
|
|
287
|
+
"selectedServices": [],
|
|
288
|
+
"requiredAccessories": [],
|
|
289
|
+
"isTradeIn": False,
|
|
290
|
+
"isLeased": False
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
url = "https://www.bestbuy.com/productfulfillment/c/api/2.0/storeAvailability"
|
|
296
|
+
|
|
297
|
+
self.logger.debug(
|
|
298
|
+
f"Checking availability: SKU={sku}, Location={location_id}, ZIP={zipcode}"
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
try:
|
|
302
|
+
# Make request using HTTPService
|
|
303
|
+
result, error = await self.http_web.request(
|
|
304
|
+
url=url,
|
|
305
|
+
method="POST",
|
|
306
|
+
data=payload,
|
|
307
|
+
use_json=True,
|
|
308
|
+
client='httpx',
|
|
309
|
+
headers={
|
|
310
|
+
"User-Agent": random.choice(UA_LIST)
|
|
311
|
+
},
|
|
312
|
+
use_ssl=True,
|
|
313
|
+
follow_redirects=True
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if error:
|
|
317
|
+
self.logger.error(f"Error checking availability: {error}")
|
|
318
|
+
return {"error": str(error)}
|
|
319
|
+
|
|
320
|
+
if not result:
|
|
321
|
+
return {
|
|
322
|
+
"error": "No data returned from BestBuy. Service may be unavailable."
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
# Format the response
|
|
326
|
+
formatted = self._format_availability_response(
|
|
327
|
+
result,
|
|
328
|
+
location_id,
|
|
329
|
+
sku,
|
|
330
|
+
show_only_in_stock
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
return formatted
|
|
334
|
+
|
|
335
|
+
except Exception as e:
|
|
336
|
+
self.logger.error(f"Failed to check availability: {e}")
|
|
337
|
+
return {"error": str(e)}
|
|
338
|
+
|
|
339
|
+
async def find_stores(
|
|
340
|
+
self,
|
|
341
|
+
zipcode: str,
|
|
342
|
+
radius: int = 25
|
|
343
|
+
) -> Dict[str, Any]:
|
|
344
|
+
"""
|
|
345
|
+
Find BestBuy stores near a ZIP code.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
zipcode: ZIP code to search near
|
|
349
|
+
radius: Search radius in miles
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Dictionary with list of stores
|
|
353
|
+
"""
|
|
354
|
+
# Build API URL
|
|
355
|
+
url = (
|
|
356
|
+
f"https://api.bestbuy.com/v1/stores"
|
|
357
|
+
f"(area({zipcode},{radius}))"
|
|
358
|
+
f"?format=json"
|
|
359
|
+
f"&show=storeId,name,address,city,region,postalCode,phone,lat,lng,hours"
|
|
360
|
+
f"&apiKey={self.api_key}"
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
self.logger.debug(f"Finding stores near {zipcode} within {radius} miles")
|
|
364
|
+
|
|
365
|
+
try:
|
|
366
|
+
result, error = await self.http_api.request(
|
|
367
|
+
url=url,
|
|
368
|
+
method="GET",
|
|
369
|
+
client='httpx',
|
|
370
|
+
use_ssl=True
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if error:
|
|
374
|
+
self.logger.error(f"Error finding stores: {error}")
|
|
375
|
+
return {"error": str(error)}
|
|
376
|
+
|
|
377
|
+
stores = result.get('stores', [])
|
|
378
|
+
|
|
379
|
+
return {
|
|
380
|
+
"total": len(stores),
|
|
381
|
+
"stores": stores
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
except Exception as e:
|
|
385
|
+
self.logger.error(f"Failed to find stores: {e}")
|
|
386
|
+
return {"error": str(e)}
|
|
387
|
+
|
|
388
|
+
async def get_product_details(self, sku: str) -> Dict[str, Any]:
|
|
389
|
+
"""
|
|
390
|
+
Get detailed information for a specific product by SKU.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
sku: Product SKU
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
Dictionary with detailed product information
|
|
397
|
+
"""
|
|
398
|
+
url = (
|
|
399
|
+
f"https://api.bestbuy.com/v1/products/{sku}.json"
|
|
400
|
+
f"?apiKey={self.api_key}"
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
self.logger.debug(f"Getting product details for SKU: {sku}")
|
|
404
|
+
|
|
405
|
+
try:
|
|
406
|
+
result, error = await self.http_api.request(
|
|
407
|
+
url=url,
|
|
408
|
+
method="GET",
|
|
409
|
+
client='httpx',
|
|
410
|
+
use_ssl=True
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
if error:
|
|
414
|
+
self.logger.error(f"Error getting product details: {error}")
|
|
415
|
+
return {"error": str(error)}
|
|
416
|
+
|
|
417
|
+
return result
|
|
418
|
+
|
|
419
|
+
except Exception as e:
|
|
420
|
+
self.logger.error(f"Failed to get product details: {e}")
|
|
421
|
+
return {"error": str(e)}
|
|
422
|
+
|
|
423
|
+
def _format_availability_response(
|
|
424
|
+
self,
|
|
425
|
+
result: Dict[str, Any],
|
|
426
|
+
location_id: str,
|
|
427
|
+
sku: str,
|
|
428
|
+
show_only_in_stock: bool = False
|
|
429
|
+
) -> Dict[str, Any]:
|
|
430
|
+
"""
|
|
431
|
+
Format availability response into structured data.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
result: Raw API response
|
|
435
|
+
location_id: Store location ID
|
|
436
|
+
sku: Product SKU
|
|
437
|
+
show_only_in_stock: Filter flag
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
Formatted availability dictionary
|
|
441
|
+
"""
|
|
442
|
+
try:
|
|
443
|
+
# Extract store information from ISPU locations
|
|
444
|
+
locations = result.get("ispu", {}).get("locations", [])
|
|
445
|
+
store = next(
|
|
446
|
+
(loc for loc in locations if loc.get("id") == location_id),
|
|
447
|
+
None
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
if not store:
|
|
451
|
+
return {
|
|
452
|
+
"error": "No matching store location found",
|
|
453
|
+
"location_id": location_id
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
# Extract store details
|
|
457
|
+
store_info = {
|
|
458
|
+
"store_id": location_id,
|
|
459
|
+
"name": store.get("name", "N/A"),
|
|
460
|
+
"address": store.get("address", "N/A"),
|
|
461
|
+
"city": store.get("city", "N/A"),
|
|
462
|
+
"state": store.get("state", "N/A"),
|
|
463
|
+
"zip_code": store.get("zipCode", "N/A"),
|
|
464
|
+
"latitude": store.get("latitude", "N/A"),
|
|
465
|
+
"longitude": store.get("longitude", "N/A"),
|
|
466
|
+
"hours": store.get("openTimesMap", {})
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
# Extract product availability from ISPU items
|
|
470
|
+
items = result.get("ispu", {}).get("items", [])
|
|
471
|
+
item = next(
|
|
472
|
+
(it for it in items if it.get("sku") == sku),
|
|
473
|
+
None
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
if not item:
|
|
477
|
+
return {
|
|
478
|
+
"error": "No matching product found",
|
|
479
|
+
"sku": sku,
|
|
480
|
+
"store": store_info
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
# Extract item-level availability
|
|
484
|
+
item_locations = item.get("locations", [])
|
|
485
|
+
availability = next(
|
|
486
|
+
(loc for loc in item_locations if loc.get("locationId") == location_id),
|
|
487
|
+
None
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
if not availability:
|
|
491
|
+
return {
|
|
492
|
+
"error": "No availability data for this product at this location",
|
|
493
|
+
"sku": sku,
|
|
494
|
+
"store": store_info
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
# Build product availability info
|
|
498
|
+
in_store_availability = availability.get("inStoreAvailability", {})
|
|
499
|
+
product_info = {
|
|
500
|
+
"sku": sku,
|
|
501
|
+
"in_store_available": item.get("inStoreAvailable", False),
|
|
502
|
+
"pickup_eligible": item.get("pickupEligible", False),
|
|
503
|
+
"on_shelf_display": availability.get("onShelfDisplay", False),
|
|
504
|
+
"available_quantity": in_store_availability.get("availableInStoreQuantity", 0),
|
|
505
|
+
"available_from": in_store_availability.get("minDate"),
|
|
506
|
+
"pickup_types": item.get("pickupTypes", [])
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
# Check if we should filter out of stock
|
|
510
|
+
if show_only_in_stock and product_info["available_quantity"] == 0:
|
|
511
|
+
return {
|
|
512
|
+
"message": "Product not in stock at this location",
|
|
513
|
+
"sku": sku,
|
|
514
|
+
"store": store_info,
|
|
515
|
+
"product": product_info
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
"store": store_info,
|
|
520
|
+
"product": product_info,
|
|
521
|
+
"status": "available" if product_info["available_quantity"] > 0 else "unavailable"
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
except Exception as e:
|
|
525
|
+
self.logger.error(f"Error formatting availability response: {e}")
|
|
526
|
+
return {
|
|
527
|
+
"error": f"Failed to format response: {str(e)}"
|
|
528
|
+
}
|