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,703 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI-Parrot gVisor Sandbox Tool
|
|
3
|
+
|
|
4
|
+
Secure Python execution tool using gVisor (runsc) for complete kernel-level isolation.
|
|
5
|
+
This tool provides safe code execution for untrusted LLM-generated code.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import tempfile
|
|
12
|
+
import uuid
|
|
13
|
+
import shutil
|
|
14
|
+
import subprocess
|
|
15
|
+
import logging
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any, Dict, Optional, List, Tuple, Union
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from contextlib import contextmanager
|
|
20
|
+
import base64
|
|
21
|
+
import pickle
|
|
22
|
+
import pandas as pd
|
|
23
|
+
import numpy as np
|
|
24
|
+
|
|
25
|
+
from parrot.tools.abstract import AbstractTool, ToolResult
|
|
26
|
+
from parrot.tools.pythonpandas import PythonPandasTool
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class SandboxConfig:
|
|
31
|
+
"""Configuration for gVisor sandbox"""
|
|
32
|
+
runtime: str = "runsc"
|
|
33
|
+
network: str = "none" # Disable network by default
|
|
34
|
+
max_memory: str = "2G"
|
|
35
|
+
max_cpu: float = 2.0
|
|
36
|
+
timeout: int = 30
|
|
37
|
+
python_image: str = "python:3.11-slim"
|
|
38
|
+
enable_gpu: bool = False
|
|
39
|
+
mount_paths: List[str] = field(default_factory=list)
|
|
40
|
+
pip_packages: List[str] = field(default_factory=lambda: [
|
|
41
|
+
"pandas", "numpy", "matplotlib", "seaborn", "scikit-learn",
|
|
42
|
+
"scipy", "plotly", "jupyterlab", "ipykernel"
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class ExecutionResult:
|
|
48
|
+
"""Result from sandbox execution"""
|
|
49
|
+
success: bool
|
|
50
|
+
stdout: str
|
|
51
|
+
stderr: str
|
|
52
|
+
exit_code: int
|
|
53
|
+
execution_time_ms: float
|
|
54
|
+
files_created: Dict[str, bytes] = field(default_factory=dict)
|
|
55
|
+
dataframes: Dict[str, pd.DataFrame] = field(default_factory=dict)
|
|
56
|
+
plots: List[Dict[str, Any]] = field(default_factory=list)
|
|
57
|
+
notebook_path: Optional[str] = None
|
|
58
|
+
error_message: Optional[str] = None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SandboxTool(AbstractTool):
|
|
62
|
+
"""
|
|
63
|
+
Secure Python execution using gVisor sandbox.
|
|
64
|
+
|
|
65
|
+
This tool provides kernel-level isolation for executing untrusted code
|
|
66
|
+
generated by LLMs, preventing escape attempts and resource exhaustion.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
name = "gvisor_python_sandbox"
|
|
70
|
+
description = "Execute Python code in a secure gVisor sandbox with kernel isolation"
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
config: Optional[SandboxConfig] = None,
|
|
75
|
+
workspace_dir: Optional[str] = None,
|
|
76
|
+
dataframes: Optional[Dict[str, pd.DataFrame]] = None,
|
|
77
|
+
enable_jupyter: bool = False,
|
|
78
|
+
logger: Optional[logging.Logger] = None
|
|
79
|
+
):
|
|
80
|
+
"""
|
|
81
|
+
Initialize gVisor sandbox tool.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
config: Sandbox configuration
|
|
85
|
+
workspace_dir: Directory for temporary files
|
|
86
|
+
dataframes: Initial dataframes to make available
|
|
87
|
+
enable_jupyter: Enable Jupyter notebook creation
|
|
88
|
+
logger: Logger instance
|
|
89
|
+
"""
|
|
90
|
+
super().__init__()
|
|
91
|
+
self.config = config or SandboxConfig()
|
|
92
|
+
self.workspace_dir = Path(workspace_dir or tempfile.mkdtemp(prefix="gvisor_"))
|
|
93
|
+
self.workspace_dir.mkdir(parents=True, exist_ok=True)
|
|
94
|
+
|
|
95
|
+
self.dataframes = dataframes or {}
|
|
96
|
+
self.enable_jupyter = enable_jupyter
|
|
97
|
+
self.logger = logger or logging.getLogger(__name__)
|
|
98
|
+
|
|
99
|
+
# Verify gVisor installation
|
|
100
|
+
self._verify_installation()
|
|
101
|
+
|
|
102
|
+
# Prepare base container image
|
|
103
|
+
self._prepare_base_image()
|
|
104
|
+
|
|
105
|
+
def _verify_installation(self):
|
|
106
|
+
"""Verify gVisor and containerd are installed"""
|
|
107
|
+
try:
|
|
108
|
+
# Check runsc
|
|
109
|
+
result = subprocess.run(
|
|
110
|
+
["runsc", "--version"],
|
|
111
|
+
capture_output=True,
|
|
112
|
+
text=True
|
|
113
|
+
)
|
|
114
|
+
if result.returncode != 0:
|
|
115
|
+
raise RuntimeError("runsc not found. Please install gVisor.")
|
|
116
|
+
|
|
117
|
+
self.logger.info(f"gVisor version: {result.stdout.strip()}")
|
|
118
|
+
|
|
119
|
+
# Check containerd
|
|
120
|
+
result = subprocess.run(
|
|
121
|
+
["ctr", "--version"],
|
|
122
|
+
capture_output=True,
|
|
123
|
+
text=True
|
|
124
|
+
)
|
|
125
|
+
if result.returncode != 0:
|
|
126
|
+
self.logger.warning("containerd not found. Using Docker as fallback.")
|
|
127
|
+
self.use_docker = True
|
|
128
|
+
else:
|
|
129
|
+
self.use_docker = False
|
|
130
|
+
|
|
131
|
+
except FileNotFoundError as e:
|
|
132
|
+
raise RuntimeError(f"Required tools not installed: {e}")
|
|
133
|
+
|
|
134
|
+
def _prepare_base_image(self):
|
|
135
|
+
"""Prepare base container image with required packages"""
|
|
136
|
+
dockerfile_content = f"""
|
|
137
|
+
FROM {self.config.python_image}
|
|
138
|
+
|
|
139
|
+
# Install system dependencies
|
|
140
|
+
RUN apt-get update && apt-get install -y \\
|
|
141
|
+
gcc \\
|
|
142
|
+
g++ \\
|
|
143
|
+
make \\
|
|
144
|
+
libssl-dev \\
|
|
145
|
+
libffi-dev \\
|
|
146
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
147
|
+
|
|
148
|
+
# Install Python packages
|
|
149
|
+
RUN pip install --no-cache-dir \\
|
|
150
|
+
{' '.join(self.config.pip_packages)}
|
|
151
|
+
|
|
152
|
+
# Create working directory
|
|
153
|
+
WORKDIR /workspace
|
|
154
|
+
|
|
155
|
+
# Create sandbox user (non-root)
|
|
156
|
+
RUN useradd -m -s /bin/bash sandbox && \\
|
|
157
|
+
chown -R sandbox:sandbox /workspace
|
|
158
|
+
|
|
159
|
+
USER sandbox
|
|
160
|
+
|
|
161
|
+
# Set Python to unbuffered mode
|
|
162
|
+
ENV PYTHONUNBUFFERED=1
|
|
163
|
+
ENV PYTHONDONTWRITEBYTECODE=1
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
# Build image
|
|
167
|
+
image_name = "ai-parrot-gvisor-sandbox"
|
|
168
|
+
dockerfile_path = self.workspace_dir / "Dockerfile"
|
|
169
|
+
dockerfile_path.write_text(dockerfile_content)
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
subprocess.run(
|
|
173
|
+
["docker", "build", "-t", image_name, "-f", str(dockerfile_path), "."],
|
|
174
|
+
cwd=self.workspace_dir,
|
|
175
|
+
check=True,
|
|
176
|
+
capture_output=True
|
|
177
|
+
)
|
|
178
|
+
self.image_name = image_name
|
|
179
|
+
self.logger.info(f"Built sandbox image: {image_name}")
|
|
180
|
+
except subprocess.CalledProcessError as e:
|
|
181
|
+
self.logger.error(f"Failed to build image: {e.stderr.decode()}")
|
|
182
|
+
raise
|
|
183
|
+
|
|
184
|
+
@contextmanager
|
|
185
|
+
def _create_sandbox_container(self, code: str, session_id: str):
|
|
186
|
+
"""Create and manage a gVisor sandbox container"""
|
|
187
|
+
container_name = f"sandbox-{session_id}"
|
|
188
|
+
exec_dir = self.workspace_dir / session_id
|
|
189
|
+
exec_dir.mkdir(parents=True, exist_ok=True)
|
|
190
|
+
|
|
191
|
+
try:
|
|
192
|
+
# Prepare execution script
|
|
193
|
+
script_path = exec_dir / "execute.py"
|
|
194
|
+
script_path.write_text(self._prepare_execution_script(code))
|
|
195
|
+
|
|
196
|
+
# Prepare data files
|
|
197
|
+
self._prepare_data_files(exec_dir)
|
|
198
|
+
|
|
199
|
+
# Create container with gVisor runtime
|
|
200
|
+
run_cmd = [
|
|
201
|
+
"docker", "run",
|
|
202
|
+
"--runtime", self.config.runtime, # Use gVisor runtime
|
|
203
|
+
"--name", container_name,
|
|
204
|
+
"-d", # Detached
|
|
205
|
+
"--rm", # Auto-remove
|
|
206
|
+
"--memory", self.config.max_memory,
|
|
207
|
+
"--cpus", str(self.config.max_cpu),
|
|
208
|
+
"--network", self.config.network,
|
|
209
|
+
"--security-opt", "no-new-privileges",
|
|
210
|
+
"--cap-drop", "ALL", # Drop all capabilities
|
|
211
|
+
"-v", f"{exec_dir}:/workspace:ro", # Read-only mount
|
|
212
|
+
"-v", f"{exec_dir}/output:/output:rw", # Output directory
|
|
213
|
+
self.image_name,
|
|
214
|
+
"python", "/workspace/execute.py"
|
|
215
|
+
]
|
|
216
|
+
|
|
217
|
+
# Start container
|
|
218
|
+
subprocess.run(run_cmd, check=True, capture_output=True)
|
|
219
|
+
|
|
220
|
+
yield container_name
|
|
221
|
+
|
|
222
|
+
finally:
|
|
223
|
+
# Cleanup container
|
|
224
|
+
try:
|
|
225
|
+
subprocess.run(
|
|
226
|
+
["docker", "stop", container_name],
|
|
227
|
+
capture_output=True,
|
|
228
|
+
timeout=5
|
|
229
|
+
)
|
|
230
|
+
except:
|
|
231
|
+
subprocess.run(
|
|
232
|
+
["docker", "kill", container_name],
|
|
233
|
+
capture_output=True
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def _prepare_execution_script(self, code: str) -> str:
|
|
237
|
+
"""Prepare the Python script for execution in sandbox"""
|
|
238
|
+
return f'''
|
|
239
|
+
import sys
|
|
240
|
+
import os
|
|
241
|
+
import json
|
|
242
|
+
import pickle
|
|
243
|
+
import base64
|
|
244
|
+
import traceback
|
|
245
|
+
import pandas as pd
|
|
246
|
+
import numpy as np
|
|
247
|
+
import matplotlib
|
|
248
|
+
matplotlib.use('Agg') # Non-interactive backend
|
|
249
|
+
import matplotlib.pyplot as plt
|
|
250
|
+
from datetime import datetime
|
|
251
|
+
|
|
252
|
+
# Load serialized dataframes
|
|
253
|
+
dataframes_file = '/workspace/dataframes.pkl'
|
|
254
|
+
if os.path.exists(dataframes_file):
|
|
255
|
+
with open(dataframes_file, 'rb') as f:
|
|
256
|
+
dataframes = pickle.load(f)
|
|
257
|
+
# Inject dataframes into global namespace
|
|
258
|
+
for name, df in dataframes.items():
|
|
259
|
+
globals()[name] = df
|
|
260
|
+
print(f"Loaded dataframe: {{name}} with shape {{df.shape}}")
|
|
261
|
+
|
|
262
|
+
# Execution metadata
|
|
263
|
+
execution_start = datetime.now()
|
|
264
|
+
created_files = {{}}
|
|
265
|
+
output_dataframes = {{}}
|
|
266
|
+
plots = []
|
|
267
|
+
|
|
268
|
+
# Redirect stdout to capture prints
|
|
269
|
+
import io
|
|
270
|
+
from contextlib import redirect_stdout, redirect_stderr
|
|
271
|
+
|
|
272
|
+
stdout_capture = io.StringIO()
|
|
273
|
+
stderr_capture = io.StringIO()
|
|
274
|
+
|
|
275
|
+
# Custom figure save wrapper
|
|
276
|
+
original_savefig = plt.savefig
|
|
277
|
+
def wrapped_savefig(fname, **kwargs):
|
|
278
|
+
fname = f"/output/{{fname}}"
|
|
279
|
+
original_savefig(fname, **kwargs)
|
|
280
|
+
with open(fname, 'rb') as f:
|
|
281
|
+
created_files[os.path.basename(fname)] = base64.b64encode(f.read()).decode()
|
|
282
|
+
plots.append({{"type": "matplotlib", "filename": os.path.basename(fname)}})
|
|
283
|
+
return fname
|
|
284
|
+
plt.savefig = wrapped_savefig
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
with redirect_stdout(stdout_capture), redirect_stderr(stderr_capture):
|
|
288
|
+
# Execute user code
|
|
289
|
+
exec("""
|
|
290
|
+
{code}
|
|
291
|
+
""")
|
|
292
|
+
|
|
293
|
+
# Capture any created dataframes
|
|
294
|
+
for name, obj in list(globals().items()):
|
|
295
|
+
if isinstance(obj, pd.DataFrame) and name not in ['dataframes', 'pd']:
|
|
296
|
+
if name not in dataframes or not obj.equals(dataframes.get(name, pd.DataFrame())):
|
|
297
|
+
output_dataframes[name] = obj
|
|
298
|
+
|
|
299
|
+
execution_time = (datetime.now() - execution_start).total_seconds() * 1000
|
|
300
|
+
|
|
301
|
+
# Save results
|
|
302
|
+
result = {{
|
|
303
|
+
"success": True,
|
|
304
|
+
"stdout": stdout_capture.getvalue(),
|
|
305
|
+
"stderr": stderr_capture.getvalue(),
|
|
306
|
+
"exit_code": 0,
|
|
307
|
+
"execution_time_ms": execution_time,
|
|
308
|
+
"files_created": created_files,
|
|
309
|
+
"dataframes": {{name: df.to_dict('records') for name, df in output_dataframes.items()}},
|
|
310
|
+
"plots": plots
|
|
311
|
+
}}
|
|
312
|
+
|
|
313
|
+
except Exception as e:
|
|
314
|
+
result = {{
|
|
315
|
+
"success": False,
|
|
316
|
+
"stdout": stdout_capture.getvalue(),
|
|
317
|
+
"stderr": stderr_capture.getvalue() + "\\n" + traceback.format_exc(),
|
|
318
|
+
"exit_code": 1,
|
|
319
|
+
"execution_time_ms": (datetime.now() - execution_start).total_seconds() * 1000,
|
|
320
|
+
"error_message": str(e)
|
|
321
|
+
}}
|
|
322
|
+
|
|
323
|
+
# Write result
|
|
324
|
+
with open('/output/result.json', 'w') as f:
|
|
325
|
+
json.dump(result, f)
|
|
326
|
+
'''
|
|
327
|
+
|
|
328
|
+
def _prepare_data_files(self, exec_dir: Path):
|
|
329
|
+
"""Prepare data files for the sandbox"""
|
|
330
|
+
# Create output directory
|
|
331
|
+
output_dir = exec_dir / "output"
|
|
332
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
333
|
+
|
|
334
|
+
# Serialize dataframes
|
|
335
|
+
if self.dataframes:
|
|
336
|
+
with open(exec_dir / "dataframes.pkl", 'wb') as f:
|
|
337
|
+
pickle.dump(self.dataframes, f)
|
|
338
|
+
|
|
339
|
+
async def execute(
|
|
340
|
+
self,
|
|
341
|
+
code: str,
|
|
342
|
+
description: str = "Execute Python code in gVisor sandbox",
|
|
343
|
+
create_notebook: bool = False,
|
|
344
|
+
**kwargs
|
|
345
|
+
) -> ToolResult:
|
|
346
|
+
"""
|
|
347
|
+
Execute Python code in gVisor sandbox.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
code: Python code to execute
|
|
351
|
+
description: Description of execution
|
|
352
|
+
create_notebook: Create Jupyter notebook of execution
|
|
353
|
+
**kwargs: Additional parameters
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
ToolResult with execution results
|
|
357
|
+
"""
|
|
358
|
+
session_id = str(uuid.uuid4())[:8]
|
|
359
|
+
self.logger.info(f"Starting sandbox execution: {session_id}")
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
# Execute in sandbox
|
|
363
|
+
result = await self._execute_in_sandbox(code, session_id)
|
|
364
|
+
|
|
365
|
+
# Optionally create notebook
|
|
366
|
+
if create_notebook and self.enable_jupyter:
|
|
367
|
+
notebook_path = await self._create_notebook(code, result, session_id)
|
|
368
|
+
result.notebook_path = notebook_path
|
|
369
|
+
|
|
370
|
+
# Format response
|
|
371
|
+
response = self._format_response(result)
|
|
372
|
+
|
|
373
|
+
return ToolResult(
|
|
374
|
+
result=response,
|
|
375
|
+
status="success" if result.success else "error",
|
|
376
|
+
error=result.error_message,
|
|
377
|
+
metadata={
|
|
378
|
+
"session_id": session_id,
|
|
379
|
+
"execution_time_ms": result.execution_time_ms,
|
|
380
|
+
"files_created": list(result.files_created.keys()),
|
|
381
|
+
"dataframes_created": list(result.dataframes.keys()),
|
|
382
|
+
"notebook_path": result.notebook_path
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
except Exception as e:
|
|
387
|
+
self.logger.error(f"Sandbox execution failed: {e}")
|
|
388
|
+
return ToolResult(
|
|
389
|
+
result=None,
|
|
390
|
+
status="error",
|
|
391
|
+
error=str(e)
|
|
392
|
+
)
|
|
393
|
+
finally:
|
|
394
|
+
# Cleanup session files
|
|
395
|
+
if not kwargs.get("keep_files", False):
|
|
396
|
+
self._cleanup_session(session_id)
|
|
397
|
+
|
|
398
|
+
async def _execute_in_sandbox(self, code: str, session_id: str) -> ExecutionResult:
|
|
399
|
+
"""Execute code in gVisor sandbox container"""
|
|
400
|
+
exec_dir = self.workspace_dir / session_id
|
|
401
|
+
|
|
402
|
+
with self._create_sandbox_container(code, session_id) as container_name:
|
|
403
|
+
# Wait for execution with timeout
|
|
404
|
+
try:
|
|
405
|
+
result = subprocess.run(
|
|
406
|
+
["docker", "wait", container_name],
|
|
407
|
+
capture_output=True,
|
|
408
|
+
text=True,
|
|
409
|
+
timeout=self.config.timeout
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
# Get logs
|
|
413
|
+
logs = subprocess.run(
|
|
414
|
+
["docker", "logs", container_name],
|
|
415
|
+
capture_output=True,
|
|
416
|
+
text=True
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
except subprocess.TimeoutExpired:
|
|
420
|
+
# Kill container on timeout
|
|
421
|
+
subprocess.run(["docker", "kill", container_name], capture_output=True)
|
|
422
|
+
return ExecutionResult(
|
|
423
|
+
success=False,
|
|
424
|
+
stdout="",
|
|
425
|
+
stderr="Execution timeout exceeded",
|
|
426
|
+
exit_code=-1,
|
|
427
|
+
execution_time_ms=self.config.timeout * 1000,
|
|
428
|
+
error_message=f"Code execution exceeded {self.config.timeout}s timeout"
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
# Read results
|
|
432
|
+
result_file = exec_dir / "output" / "result.json"
|
|
433
|
+
if result_file.exists():
|
|
434
|
+
with open(result_file, 'r') as f:
|
|
435
|
+
result_data = json.load(f)
|
|
436
|
+
|
|
437
|
+
# Decode file contents
|
|
438
|
+
files_created = {}
|
|
439
|
+
for filename, content in result_data.get("files_created", {}).items():
|
|
440
|
+
files_created[filename] = base64.b64decode(content)
|
|
441
|
+
|
|
442
|
+
# Reconstruct dataframes
|
|
443
|
+
dataframes = {}
|
|
444
|
+
for name, records in result_data.get("dataframes", {}).items():
|
|
445
|
+
dataframes[name] = pd.DataFrame(records)
|
|
446
|
+
|
|
447
|
+
return ExecutionResult(
|
|
448
|
+
success=result_data["success"],
|
|
449
|
+
stdout=result_data["stdout"],
|
|
450
|
+
stderr=result_data["stderr"],
|
|
451
|
+
exit_code=result_data["exit_code"],
|
|
452
|
+
execution_time_ms=result_data["execution_time_ms"],
|
|
453
|
+
files_created=files_created,
|
|
454
|
+
dataframes=dataframes,
|
|
455
|
+
plots=result_data.get("plots", []),
|
|
456
|
+
error_message=result_data.get("error_message")
|
|
457
|
+
)
|
|
458
|
+
else:
|
|
459
|
+
return ExecutionResult(
|
|
460
|
+
success=False,
|
|
461
|
+
stdout=logs.stdout,
|
|
462
|
+
stderr=logs.stderr,
|
|
463
|
+
exit_code=result.returncode,
|
|
464
|
+
execution_time_ms=0,
|
|
465
|
+
error_message="Failed to read execution results"
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
async def _create_notebook(
|
|
469
|
+
self,
|
|
470
|
+
code: str,
|
|
471
|
+
result: ExecutionResult,
|
|
472
|
+
session_id: str
|
|
473
|
+
) -> str:
|
|
474
|
+
"""Create Jupyter notebook from execution"""
|
|
475
|
+
import nbformat
|
|
476
|
+
from nbformat.v4 import new_notebook, new_code_cell, new_markdown_cell
|
|
477
|
+
|
|
478
|
+
# Create notebook
|
|
479
|
+
nb = new_notebook()
|
|
480
|
+
|
|
481
|
+
# Add header cell
|
|
482
|
+
nb.cells.append(new_markdown_cell(
|
|
483
|
+
f"# gVisor Sandbox Execution\n"
|
|
484
|
+
f"Session: {session_id}\n"
|
|
485
|
+
f"Execution Time: {result.execution_time_ms:.2f}ms"
|
|
486
|
+
))
|
|
487
|
+
|
|
488
|
+
# Add code cell
|
|
489
|
+
code_cell = new_code_cell(code)
|
|
490
|
+
if result.stdout:
|
|
491
|
+
code_cell.outputs = [{
|
|
492
|
+
"output_type": "stream",
|
|
493
|
+
"name": "stdout",
|
|
494
|
+
"text": result.stdout
|
|
495
|
+
}]
|
|
496
|
+
nb.cells.append(code_cell)
|
|
497
|
+
|
|
498
|
+
# Add results cell
|
|
499
|
+
if result.dataframes:
|
|
500
|
+
nb.cells.append(new_markdown_cell("## Created DataFrames"))
|
|
501
|
+
for name, df in result.dataframes.items():
|
|
502
|
+
nb.cells.append(new_code_cell(f"# DataFrame: {name}\n{df.to_string()}"))
|
|
503
|
+
|
|
504
|
+
if result.plots:
|
|
505
|
+
nb.cells.append(new_markdown_cell("## Generated Plots"))
|
|
506
|
+
for plot in result.plots:
|
|
507
|
+
nb.cells.append(new_markdown_cell(f"![{plot['filename']}]({plot['filename']})"))
|
|
508
|
+
|
|
509
|
+
# Save notebook
|
|
510
|
+
notebook_path = self.workspace_dir / f"notebook_{session_id}.ipynb"
|
|
511
|
+
with open(notebook_path, 'w') as f:
|
|
512
|
+
nbformat.write(nb, f)
|
|
513
|
+
|
|
514
|
+
return str(notebook_path)
|
|
515
|
+
|
|
516
|
+
def _format_response(self, result: ExecutionResult) -> str:
|
|
517
|
+
"""Format execution result for display"""
|
|
518
|
+
parts = []
|
|
519
|
+
|
|
520
|
+
if result.success:
|
|
521
|
+
parts.append("✓ Code executed successfully")
|
|
522
|
+
parts.append(f"Execution time: {result.execution_time_ms:.2f}ms")
|
|
523
|
+
else:
|
|
524
|
+
parts.append("✗ Code execution failed")
|
|
525
|
+
parts.append(f"Error: {result.error_message}")
|
|
526
|
+
|
|
527
|
+
if result.stdout:
|
|
528
|
+
parts.append("\n--- Output ---")
|
|
529
|
+
parts.append(result.stdout)
|
|
530
|
+
|
|
531
|
+
if result.stderr and not result.success:
|
|
532
|
+
parts.append("\n--- Errors ---")
|
|
533
|
+
parts.append(result.stderr)
|
|
534
|
+
|
|
535
|
+
if result.files_created:
|
|
536
|
+
parts.append(f"\n--- Files Created ({len(result.files_created)}) ---")
|
|
537
|
+
for filename in result.files_created.keys():
|
|
538
|
+
parts.append(f" • {filename}")
|
|
539
|
+
|
|
540
|
+
if result.dataframes:
|
|
541
|
+
parts.append(f"\n--- DataFrames Created ({len(result.dataframes)}) ---")
|
|
542
|
+
for name, df in result.dataframes.items():
|
|
543
|
+
parts.append(f" • {name}: {df.shape[0]} rows × {df.shape[1]} columns")
|
|
544
|
+
|
|
545
|
+
if result.notebook_path:
|
|
546
|
+
parts.append(f"\n--- Notebook ---")
|
|
547
|
+
parts.append(f" 📓 {result.notebook_path}")
|
|
548
|
+
|
|
549
|
+
return "\n".join(parts)
|
|
550
|
+
|
|
551
|
+
def _cleanup_session(self, session_id: str):
|
|
552
|
+
"""Clean up session files"""
|
|
553
|
+
session_dir = self.workspace_dir / session_id
|
|
554
|
+
if session_dir.exists():
|
|
555
|
+
shutil.rmtree(session_dir)
|
|
556
|
+
|
|
557
|
+
def add_dataframe(self, name: str, df: pd.DataFrame):
|
|
558
|
+
"""Add a dataframe to be available in the sandbox"""
|
|
559
|
+
self.dataframes[name] = df
|
|
560
|
+
|
|
561
|
+
def set_timeout(self, timeout: int):
|
|
562
|
+
"""Set execution timeout in seconds"""
|
|
563
|
+
self.config.timeout = timeout
|
|
564
|
+
|
|
565
|
+
def set_memory_limit(self, memory: str):
|
|
566
|
+
"""Set memory limit (e.g., '1G', '512M')"""
|
|
567
|
+
self.config.max_memory = memory
|
|
568
|
+
|
|
569
|
+
def cleanup(self):
|
|
570
|
+
"""Clean up all resources"""
|
|
571
|
+
if self.workspace_dir.exists():
|
|
572
|
+
shutil.rmtree(self.workspace_dir)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
class SandboxPandasTool(SandboxTool):
|
|
576
|
+
"""
|
|
577
|
+
Specialized version for Pandas operations with enhanced data handling.
|
|
578
|
+
Drop-in replacement for PythonPandasTool with security.
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
name = "sandbox_pandas_tool"
|
|
582
|
+
description = "Secure Pandas operations in gVisor sandbox"
|
|
583
|
+
|
|
584
|
+
def __init__(self, dataframes: Optional[Dict[str, pd.DataFrame]] = None, **kwargs):
|
|
585
|
+
"""Initialize with Pandas-specific configuration"""
|
|
586
|
+
config = SandboxConfig(
|
|
587
|
+
pip_packages=[
|
|
588
|
+
"pandas", "numpy", "matplotlib", "seaborn",
|
|
589
|
+
"plotly", "scikit-learn", "scipy", "statsmodels",
|
|
590
|
+
"openpyxl", "xlrd", "pyarrow", "fastparquet"
|
|
591
|
+
]
|
|
592
|
+
)
|
|
593
|
+
super().__init__(config=config, dataframes=dataframes, **kwargs)
|
|
594
|
+
|
|
595
|
+
async def analyze_dataframe(
|
|
596
|
+
self,
|
|
597
|
+
df_name: str,
|
|
598
|
+
analysis_type: str = "summary"
|
|
599
|
+
) -> ToolResult:
|
|
600
|
+
"""
|
|
601
|
+
Perform automated DataFrame analysis.
|
|
602
|
+
|
|
603
|
+
Args:
|
|
604
|
+
df_name: Name of dataframe to analyze
|
|
605
|
+
analysis_type: Type of analysis (summary, correlation, distribution)
|
|
606
|
+
"""
|
|
607
|
+
if df_name not in self.dataframes:
|
|
608
|
+
return ToolResult(
|
|
609
|
+
result=None,
|
|
610
|
+
status="error",
|
|
611
|
+
error=f"DataFrame '{df_name}' not found"
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
analysis_code = {
|
|
615
|
+
"summary": f"""
|
|
616
|
+
import pandas as pd
|
|
617
|
+
df = {df_name}
|
|
618
|
+
print("=== DataFrame Summary ===")
|
|
619
|
+
print(f"Shape: {{df.shape}}")
|
|
620
|
+
print(f"\\nData Types:\\n{{df.dtypes}}")
|
|
621
|
+
print(f"\\nSummary Statistics:\\n{{df.describe()}}")
|
|
622
|
+
print(f"\\nMissing Values:\\n{{df.isnull().sum()}}")
|
|
623
|
+
print(f"\\nMemory Usage:\\n{{df.memory_usage(deep=True)}}")
|
|
624
|
+
""",
|
|
625
|
+
|
|
626
|
+
"correlation": f"""
|
|
627
|
+
import pandas as pd
|
|
628
|
+
import matplotlib.pyplot as plt
|
|
629
|
+
import seaborn as sns
|
|
630
|
+
|
|
631
|
+
df = {df_name}
|
|
632
|
+
numeric_cols = df.select_dtypes(include=['number']).columns
|
|
633
|
+
if len(numeric_cols) > 1:
|
|
634
|
+
corr_matrix = df[numeric_cols].corr()
|
|
635
|
+
print("=== Correlation Matrix ===")
|
|
636
|
+
print(corr_matrix)
|
|
637
|
+
|
|
638
|
+
plt.figure(figsize=(10, 8))
|
|
639
|
+
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm', center=0)
|
|
640
|
+
plt.title('Correlation Heatmap')
|
|
641
|
+
plt.tight_layout()
|
|
642
|
+
plt.savefig('correlation_heatmap.png')
|
|
643
|
+
else:
|
|
644
|
+
print("Not enough numeric columns for correlation analysis")
|
|
645
|
+
""",
|
|
646
|
+
|
|
647
|
+
"distribution": f"""
|
|
648
|
+
import pandas as pd
|
|
649
|
+
import matplotlib.pyplot as plt
|
|
650
|
+
import seaborn as sns
|
|
651
|
+
|
|
652
|
+
df = {df_name}
|
|
653
|
+
numeric_cols = df.select_dtypes(include=['number']).columns
|
|
654
|
+
|
|
655
|
+
for i, col in enumerate(numeric_cols[:6]): # Limit to first 6 columns
|
|
656
|
+
plt.figure(figsize=(12, 4))
|
|
657
|
+
|
|
658
|
+
plt.subplot(1, 3, 1)
|
|
659
|
+
df[col].hist(bins=30, edgecolor='black')
|
|
660
|
+
plt.title(f'{{col}} - Histogram')
|
|
661
|
+
|
|
662
|
+
plt.subplot(1, 3, 2)
|
|
663
|
+
df.boxplot(column=col)
|
|
664
|
+
plt.title(f'{{col}} - Boxplot')
|
|
665
|
+
|
|
666
|
+
plt.subplot(1, 3, 3)
|
|
667
|
+
df[col].plot(kind='kde')
|
|
668
|
+
plt.title(f'{{col}} - KDE')
|
|
669
|
+
|
|
670
|
+
plt.tight_layout()
|
|
671
|
+
plt.savefig(f'distribution_{{col}}.png')
|
|
672
|
+
plt.close()
|
|
673
|
+
|
|
674
|
+
print(f"Generated distribution plots for {{len(numeric_cols)}} numeric columns")
|
|
675
|
+
"""
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
code = analysis_code.get(analysis_type, analysis_code["summary"])
|
|
679
|
+
return await self.execute(
|
|
680
|
+
code,
|
|
681
|
+
description=f"Analyze DataFrame '{df_name}' - {analysis_type}"
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
# Factory function for easy instantiation
|
|
686
|
+
def create_sandbox_tool(
|
|
687
|
+
tool_type: str = "sandbox",
|
|
688
|
+
**kwargs
|
|
689
|
+
) -> Union[SandboxTool, SandboxPandasTool]:
|
|
690
|
+
"""
|
|
691
|
+
Factory function to create gVisor tools.
|
|
692
|
+
|
|
693
|
+
Args:
|
|
694
|
+
tool_type: Type of tool ('sandbox' or 'pandas')
|
|
695
|
+
**kwargs: Configuration parameters
|
|
696
|
+
|
|
697
|
+
Returns:
|
|
698
|
+
Configured gVisor tool instance
|
|
699
|
+
"""
|
|
700
|
+
if tool_type == "pandas":
|
|
701
|
+
return SandboxPandasTool(**kwargs)
|
|
702
|
+
else:
|
|
703
|
+
return SandboxTool(**kwargs)
|