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,76 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import BinaryIO, Optional, List, Union
|
|
3
|
+
from io import BytesIO, StringIO
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class FileMetadata:
|
|
10
|
+
"""Metadata for a file in the file manager"""
|
|
11
|
+
name: str
|
|
12
|
+
path: str
|
|
13
|
+
size: int
|
|
14
|
+
content_type: Optional[str]
|
|
15
|
+
modified_at: Optional[datetime]
|
|
16
|
+
url: Optional[str] # Public URL if available
|
|
17
|
+
|
|
18
|
+
class FileManagerInterface(ABC):
|
|
19
|
+
"""Base interface for all file managers"""
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
async def list_files(self, path: str = "", pattern: str = "*") -> List[FileMetadata]:
|
|
23
|
+
"""List files in a directory/bucket"""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
async def get_file_url(self, path: str, expiry: int = 3600) -> str:
|
|
28
|
+
"""Get a signed/public URL for a file"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
async def upload_file(self, source: BinaryIO | Path, destination: str) -> FileMetadata:
|
|
33
|
+
"""Upload/save a file"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
async def download_file(self, source: str, destination: Path | BinaryIO) -> Path:
|
|
38
|
+
"""Download a file"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
async def copy_file(self, source: str, destination: str) -> FileMetadata:
|
|
43
|
+
"""Copy file within the same storage"""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
async def delete_file(self, path: str) -> bool:
|
|
48
|
+
"""Delete a file"""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
async def exists(self, path: str) -> bool:
|
|
53
|
+
"""Check if file exists"""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
@abstractmethod
|
|
57
|
+
async def get_file_metadata(self, path: str) -> FileMetadata:
|
|
58
|
+
"""Get metadata of a file"""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
async def create_file(self, path: str, content: bytes) -> bool:
|
|
63
|
+
"""Create a file"""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
async def create_from_text(self, path: str, text: str, encoding: str = "utf-8") -> bool:
|
|
67
|
+
"""Create a text file from string content"""
|
|
68
|
+
return await self.create_file(
|
|
69
|
+
path, text.encode(encoding)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
async def create_from_bytes(self, path: str, data: Union[bytes, BytesIO, StringIO]) -> bool:
|
|
73
|
+
"""Create a binary file from bytes content"""
|
|
74
|
+
return await self.create_file(
|
|
75
|
+
path, data.read() if hasattr(data, 'read') else data
|
|
76
|
+
)
|
parrot/tools/file/gcs.py
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
from typing import BinaryIO, Optional, List, Union
|
|
2
|
+
import mimetypes
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import logging
|
|
5
|
+
import asyncio
|
|
6
|
+
from datetime import datetime, timedelta
|
|
7
|
+
from io import BytesIO, StringIO
|
|
8
|
+
import fnmatch
|
|
9
|
+
import json
|
|
10
|
+
import google.auth
|
|
11
|
+
from google.cloud import storage
|
|
12
|
+
from google.oauth2 import service_account
|
|
13
|
+
from .abstract import FileManagerInterface, FileMetadata
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class GCSFileManager(FileManagerInterface):
|
|
17
|
+
"""File manager for Google Cloud Storage operations"""
|
|
18
|
+
|
|
19
|
+
# Resumable upload threshold (Google recommends 5MB+)
|
|
20
|
+
RESUMABLE_THRESHOLD = 5 * 1024 * 1024 # 5MB
|
|
21
|
+
CHUNK_SIZE = 256 * 1024 # 256KB upload chunks
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
bucket_name: str,
|
|
26
|
+
prefix: str = "",
|
|
27
|
+
json_credentials: Optional[dict] = None,
|
|
28
|
+
credentials: Optional[str] = None,
|
|
29
|
+
scopes: Optional[List[str]] = None,
|
|
30
|
+
project: Optional[str] = None,
|
|
31
|
+
resumable_threshold: Optional[int] = None,
|
|
32
|
+
**kwargs
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize GCS file manager.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
bucket_name: GCS bucket name
|
|
39
|
+
prefix: Default prefix/folder for all operations
|
|
40
|
+
json_credentials: Service account credentials as dict
|
|
41
|
+
credentials: Path to service account JSON file
|
|
42
|
+
scopes: OAuth2 scopes (default: cloud-platform)
|
|
43
|
+
project: GCP project ID (auto-detected if not provided)
|
|
44
|
+
resumable_threshold: File size threshold for resumable upload
|
|
45
|
+
**kwargs: Additional arguments
|
|
46
|
+
"""
|
|
47
|
+
self.bucket_name = bucket_name
|
|
48
|
+
self.prefix = prefix.rstrip('/') + '/' if prefix else ''
|
|
49
|
+
self.scopes = scopes or ['https://www.googleapis.com/auth/cloud-platform']
|
|
50
|
+
self.resumable_threshold = resumable_threshold or self.RESUMABLE_THRESHOLD
|
|
51
|
+
self.logger = logging.getLogger('ai_parrot.storage.GCS')
|
|
52
|
+
|
|
53
|
+
# Initialize credentials
|
|
54
|
+
scoped_credentials = None
|
|
55
|
+
if json_credentials:
|
|
56
|
+
# Using JSON credentials dict
|
|
57
|
+
self.credentials = service_account.Credentials.from_service_account_info(
|
|
58
|
+
json_credentials
|
|
59
|
+
)
|
|
60
|
+
self.project = project or json_credentials.get('project_id')
|
|
61
|
+
elif credentials:
|
|
62
|
+
# Using service account file
|
|
63
|
+
self.credentials = service_account.Credentials.from_service_account_file(
|
|
64
|
+
credentials
|
|
65
|
+
)
|
|
66
|
+
# Extract project from file if not provided
|
|
67
|
+
if not project:
|
|
68
|
+
with open(credentials) as f:
|
|
69
|
+
cred_data = json.load(f)
|
|
70
|
+
self.project = cred_data.get('project_id')
|
|
71
|
+
else:
|
|
72
|
+
self.project = project
|
|
73
|
+
else:
|
|
74
|
+
# Use default credentials
|
|
75
|
+
self.credentials, self.project = google.auth.default(
|
|
76
|
+
scopes=self.scopes
|
|
77
|
+
)
|
|
78
|
+
if project:
|
|
79
|
+
self.project = project
|
|
80
|
+
|
|
81
|
+
# Apply scopes if provided
|
|
82
|
+
if self.scopes and hasattr(self.credentials, 'with_scopes'):
|
|
83
|
+
scoped_credentials = self.credentials.with_scopes(self.scopes)
|
|
84
|
+
|
|
85
|
+
# Initialize GCS client
|
|
86
|
+
if scoped_credentials:
|
|
87
|
+
self.client = storage.Client(
|
|
88
|
+
credentials=scoped_credentials,
|
|
89
|
+
project=self.project
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
self.client = storage.Client(
|
|
93
|
+
credentials=self.credentials,
|
|
94
|
+
project=self.project
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
self.bucket = self.client.bucket(bucket_name)
|
|
98
|
+
|
|
99
|
+
self.logger.info(f"Started GCSFileManager for bucket: {bucket_name}")
|
|
100
|
+
|
|
101
|
+
def _resolve_path(self, path: str) -> str:
|
|
102
|
+
"""Resolve path with prefix"""
|
|
103
|
+
path = path.lstrip('/')
|
|
104
|
+
|
|
105
|
+
if self.prefix and not path.startswith(self.prefix):
|
|
106
|
+
path = self.prefix + path
|
|
107
|
+
|
|
108
|
+
return path
|
|
109
|
+
|
|
110
|
+
def _strip_prefix(self, path: str) -> str:
|
|
111
|
+
"""Remove prefix from path for display"""
|
|
112
|
+
if self.prefix and path.startswith(self.prefix):
|
|
113
|
+
return path[len(self.prefix):]
|
|
114
|
+
return path
|
|
115
|
+
|
|
116
|
+
def _blob_to_metadata(self, blob: storage.Blob) -> FileMetadata:
|
|
117
|
+
"""Convert GCS Blob to FileMetadata"""
|
|
118
|
+
# Generate signed URL (valid for 1 hour)
|
|
119
|
+
try:
|
|
120
|
+
url = blob.generate_signed_url(
|
|
121
|
+
version="v4",
|
|
122
|
+
expiration=timedelta(hours=1),
|
|
123
|
+
method="GET"
|
|
124
|
+
)
|
|
125
|
+
except Exception:
|
|
126
|
+
# Fallback to public URL if signing fails
|
|
127
|
+
url = blob.public_url
|
|
128
|
+
|
|
129
|
+
return FileMetadata(
|
|
130
|
+
name=Path(blob.name).name,
|
|
131
|
+
path=self._strip_prefix(blob.name),
|
|
132
|
+
size=blob.size or 0,
|
|
133
|
+
content_type=blob.content_type,
|
|
134
|
+
modified_at=blob.updated,
|
|
135
|
+
url=url
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
async def list_files(
|
|
139
|
+
self,
|
|
140
|
+
path: str = "",
|
|
141
|
+
pattern: str = "*"
|
|
142
|
+
) -> List[FileMetadata]:
|
|
143
|
+
"""List files matching pattern in GCS bucket"""
|
|
144
|
+
prefix = self._resolve_path(path)
|
|
145
|
+
|
|
146
|
+
def _list():
|
|
147
|
+
files = []
|
|
148
|
+
blobs = self.client.list_blobs(
|
|
149
|
+
self.bucket_name,
|
|
150
|
+
prefix=prefix
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
for blob in blobs:
|
|
154
|
+
# Skip directory markers
|
|
155
|
+
if blob.name.endswith('/'):
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
name = Path(blob.name).name
|
|
159
|
+
|
|
160
|
+
# Apply pattern matching
|
|
161
|
+
if fnmatch.fnmatch(name, pattern):
|
|
162
|
+
files.append(self._blob_to_metadata(blob))
|
|
163
|
+
|
|
164
|
+
return sorted(files, key=lambda x: x.name)
|
|
165
|
+
|
|
166
|
+
return await asyncio.to_thread(_list)
|
|
167
|
+
|
|
168
|
+
async def get_file_url(self, path: str, expiry: int = 3600) -> str:
|
|
169
|
+
"""Get signed URL for GCS blob"""
|
|
170
|
+
blob_name = self._resolve_path(path)
|
|
171
|
+
|
|
172
|
+
def _get_url():
|
|
173
|
+
blob = self.bucket.blob(blob_name)
|
|
174
|
+
|
|
175
|
+
if not blob.exists():
|
|
176
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
177
|
+
|
|
178
|
+
url = blob.generate_signed_url(
|
|
179
|
+
version="v4",
|
|
180
|
+
expiration=timedelta(seconds=expiry),
|
|
181
|
+
method="GET"
|
|
182
|
+
)
|
|
183
|
+
return url
|
|
184
|
+
|
|
185
|
+
return await asyncio.to_thread(_get_url)
|
|
186
|
+
|
|
187
|
+
async def upload_file(
|
|
188
|
+
self,
|
|
189
|
+
source: BinaryIO | Path,
|
|
190
|
+
destination: str
|
|
191
|
+
) -> FileMetadata:
|
|
192
|
+
"""
|
|
193
|
+
Upload file to GCS with automatic resumable upload for large files.
|
|
194
|
+
|
|
195
|
+
Files larger than resumable_threshold will use resumable upload.
|
|
196
|
+
"""
|
|
197
|
+
blob_name = self._resolve_path(destination)
|
|
198
|
+
|
|
199
|
+
# Guess content type
|
|
200
|
+
content_type, _ = mimetypes.guess_type(destination)
|
|
201
|
+
|
|
202
|
+
def _upload():
|
|
203
|
+
blob = self.bucket.blob(blob_name)
|
|
204
|
+
|
|
205
|
+
if content_type:
|
|
206
|
+
blob.content_type = content_type
|
|
207
|
+
|
|
208
|
+
if isinstance(source, Path):
|
|
209
|
+
file_size = source.stat().st_size
|
|
210
|
+
|
|
211
|
+
# Use resumable upload for large files
|
|
212
|
+
if file_size >= self.resumable_threshold:
|
|
213
|
+
blob.chunk_size = self.CHUNK_SIZE
|
|
214
|
+
blob.upload_from_filename(
|
|
215
|
+
str(source),
|
|
216
|
+
timeout=300 # 5 minute timeout
|
|
217
|
+
)
|
|
218
|
+
else:
|
|
219
|
+
blob.upload_from_filename(str(source))
|
|
220
|
+
else:
|
|
221
|
+
# For file objects, determine size
|
|
222
|
+
current_pos = source.tell()
|
|
223
|
+
source.seek(0, 2)
|
|
224
|
+
file_size = source.tell()
|
|
225
|
+
source.seek(current_pos)
|
|
226
|
+
|
|
227
|
+
if file_size >= self.resumable_threshold:
|
|
228
|
+
blob.chunk_size = self.CHUNK_SIZE
|
|
229
|
+
|
|
230
|
+
source.seek(0)
|
|
231
|
+
blob.upload_from_file(source, timeout=300)
|
|
232
|
+
|
|
233
|
+
# Reload blob to get updated metadata
|
|
234
|
+
blob.reload()
|
|
235
|
+
return self._blob_to_metadata(blob)
|
|
236
|
+
|
|
237
|
+
return await asyncio.to_thread(_upload)
|
|
238
|
+
|
|
239
|
+
async def download_file(
|
|
240
|
+
self,
|
|
241
|
+
source: str,
|
|
242
|
+
destination: Path | BinaryIO
|
|
243
|
+
) -> Path:
|
|
244
|
+
"""Download file from GCS"""
|
|
245
|
+
blob_name = self._resolve_path(source)
|
|
246
|
+
|
|
247
|
+
def _download():
|
|
248
|
+
blob = self.bucket.blob(blob_name)
|
|
249
|
+
|
|
250
|
+
if not blob.exists():
|
|
251
|
+
raise FileNotFoundError(f"File not found: {source}")
|
|
252
|
+
|
|
253
|
+
if isinstance(destination, Path):
|
|
254
|
+
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
255
|
+
blob.download_to_filename(str(destination))
|
|
256
|
+
return destination
|
|
257
|
+
else:
|
|
258
|
+
blob.download_to_file(destination)
|
|
259
|
+
return Path(blob_name)
|
|
260
|
+
|
|
261
|
+
return await asyncio.to_thread(_download)
|
|
262
|
+
|
|
263
|
+
async def copy_file(self, source: str, destination: str) -> FileMetadata:
|
|
264
|
+
"""Copy blob within GCS bucket"""
|
|
265
|
+
source_blob_name = self._resolve_path(source)
|
|
266
|
+
dest_blob_name = self._resolve_path(destination)
|
|
267
|
+
|
|
268
|
+
def _copy():
|
|
269
|
+
source_blob = self.bucket.blob(source_blob_name)
|
|
270
|
+
|
|
271
|
+
if not source_blob.exists():
|
|
272
|
+
raise FileNotFoundError(f"Source file not found: {source}")
|
|
273
|
+
|
|
274
|
+
# Copy blob
|
|
275
|
+
dest_blob = self.bucket.copy_blob(
|
|
276
|
+
source_blob,
|
|
277
|
+
self.bucket,
|
|
278
|
+
dest_blob_name
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
return self._blob_to_metadata(dest_blob)
|
|
282
|
+
|
|
283
|
+
return await asyncio.to_thread(_copy)
|
|
284
|
+
|
|
285
|
+
async def delete_file(self, path: str) -> bool:
|
|
286
|
+
"""Delete blob from GCS"""
|
|
287
|
+
blob_name = self._resolve_path(path)
|
|
288
|
+
|
|
289
|
+
def _delete():
|
|
290
|
+
blob = self.bucket.blob(blob_name)
|
|
291
|
+
|
|
292
|
+
if not blob.exists():
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
blob.delete()
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
return await asyncio.to_thread(_delete)
|
|
299
|
+
|
|
300
|
+
async def exists(self, path: str) -> bool:
|
|
301
|
+
"""Check if blob exists in GCS"""
|
|
302
|
+
blob_name = self._resolve_path(path)
|
|
303
|
+
|
|
304
|
+
def _check():
|
|
305
|
+
blob = self.bucket.blob(blob_name)
|
|
306
|
+
return blob.exists()
|
|
307
|
+
|
|
308
|
+
return await asyncio.to_thread(_check)
|
|
309
|
+
|
|
310
|
+
async def get_file_metadata(self, path: str) -> FileMetadata:
|
|
311
|
+
"""Get metadata of GCS blob"""
|
|
312
|
+
blob_name = self._resolve_path(path)
|
|
313
|
+
|
|
314
|
+
def _get_metadata():
|
|
315
|
+
blob = self.bucket.blob(blob_name)
|
|
316
|
+
|
|
317
|
+
if not blob.exists():
|
|
318
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
319
|
+
|
|
320
|
+
blob.reload() # Ensure we have latest metadata
|
|
321
|
+
return self._blob_to_metadata(blob)
|
|
322
|
+
|
|
323
|
+
return await asyncio.to_thread(_get_metadata)
|
|
324
|
+
|
|
325
|
+
async def create_file(self, path: str, content: bytes) -> bool:
|
|
326
|
+
"""Create blob in GCS with content"""
|
|
327
|
+
blob_name = self._resolve_path(path)
|
|
328
|
+
|
|
329
|
+
# Guess content type
|
|
330
|
+
content_type, _ = mimetypes.guess_type(path)
|
|
331
|
+
|
|
332
|
+
def _create():
|
|
333
|
+
blob = self.bucket.blob(blob_name)
|
|
334
|
+
|
|
335
|
+
if content_type:
|
|
336
|
+
blob.content_type = content_type
|
|
337
|
+
|
|
338
|
+
blob.upload_from_string(content)
|
|
339
|
+
return True
|
|
340
|
+
|
|
341
|
+
return await asyncio.to_thread(_create)
|
|
342
|
+
|
|
343
|
+
async def create_from_bytes(
|
|
344
|
+
self,
|
|
345
|
+
path: str,
|
|
346
|
+
source: Union[bytes, BytesIO, StringIO],
|
|
347
|
+
encoding: str = 'utf-8'
|
|
348
|
+
) -> FileMetadata:
|
|
349
|
+
"""Create GCS blob from bytes, BytesIO, or StringIO"""
|
|
350
|
+
blob_name = self._resolve_path(path)
|
|
351
|
+
|
|
352
|
+
# Guess content type
|
|
353
|
+
content_type, _ = mimetypes.guess_type(path)
|
|
354
|
+
|
|
355
|
+
def _create():
|
|
356
|
+
blob = self.bucket.blob(blob_name)
|
|
357
|
+
|
|
358
|
+
if content_type:
|
|
359
|
+
blob.content_type = content_type
|
|
360
|
+
|
|
361
|
+
if isinstance(source, bytes):
|
|
362
|
+
blob.upload_from_string(source)
|
|
363
|
+
elif isinstance(source, BytesIO):
|
|
364
|
+
source.seek(0)
|
|
365
|
+
blob.upload_from_file(source)
|
|
366
|
+
elif isinstance(source, StringIO):
|
|
367
|
+
source.seek(0)
|
|
368
|
+
content = source.read().encode(encoding)
|
|
369
|
+
blob.upload_from_string(content)
|
|
370
|
+
else:
|
|
371
|
+
raise TypeError(
|
|
372
|
+
f"source must be bytes, BytesIO, or StringIO, got {type(source)}"
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
blob.reload()
|
|
376
|
+
return self._blob_to_metadata(blob)
|
|
377
|
+
|
|
378
|
+
return await asyncio.to_thread(_create)
|