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,668 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DocumentDB Agent Implementation for AI-Parrot.
|
|
3
|
+
|
|
4
|
+
Concrete implementation of AbstractDBAgent for AWS DocumentDB
|
|
5
|
+
(MongoDB-compatible) with support for MQL query language.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Any, List, Optional, Union
|
|
9
|
+
import asyncio
|
|
10
|
+
import json
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pydantic import Field
|
|
13
|
+
from asyncdb import AsyncDB
|
|
14
|
+
from navconfig import config, BASE_DIR
|
|
15
|
+
|
|
16
|
+
from .abstract import (
|
|
17
|
+
AbstractDBAgent,
|
|
18
|
+
DatabaseSchema,
|
|
19
|
+
TableMetadata,
|
|
20
|
+
)
|
|
21
|
+
from ...tools.abstract import AbstractTool, ToolResult, AbstractToolArgsSchema
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CollectionQueryExecutionArgs(AbstractToolArgsSchema):
|
|
25
|
+
"""Arguments for DocumentDB collection query execution."""
|
|
26
|
+
collection_name: str = Field(description="Collection name to query")
|
|
27
|
+
query: Dict[str, Any] = Field(
|
|
28
|
+
default_factory=dict, description="MongoDB query filter"
|
|
29
|
+
)
|
|
30
|
+
limit: Optional[int] = Field(
|
|
31
|
+
default=100, description="Maximum number of documents to return"
|
|
32
|
+
)
|
|
33
|
+
projection: Optional[Dict[str, int]] = Field(
|
|
34
|
+
default=None, description="Fields to include/exclude in results"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CollectionMetadata:
|
|
39
|
+
"""Metadata for DocumentDB collections."""
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
name: str,
|
|
43
|
+
database: str,
|
|
44
|
+
document_count: int,
|
|
45
|
+
fields: List[Dict[str, str]],
|
|
46
|
+
sample_documents: List[Dict[str, Any]] = None,
|
|
47
|
+
indexes: List[Dict[str, Any]] = None
|
|
48
|
+
):
|
|
49
|
+
self.name = name
|
|
50
|
+
self.database = database
|
|
51
|
+
self.document_count = document_count
|
|
52
|
+
self.fields = fields
|
|
53
|
+
self.sample_documents = sample_documents or []
|
|
54
|
+
self.indexes = indexes or []
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DocumentDBAgent(AbstractDBAgent):
|
|
58
|
+
"""
|
|
59
|
+
DocumentDB Agent for document database introspection and MQL query generation.
|
|
60
|
+
|
|
61
|
+
Supports AWS DocumentDB (MongoDB-compatible) with MongoDB Query Language (MQL).
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
name: str = "DocumentDBAgent",
|
|
67
|
+
host: str = None,
|
|
68
|
+
port: int = 27017,
|
|
69
|
+
database: str = None,
|
|
70
|
+
username: str = None,
|
|
71
|
+
password: str = None,
|
|
72
|
+
credentials: Union[str, Dict[str, Any]] = None,
|
|
73
|
+
ssl: bool = True,
|
|
74
|
+
tls_ca_file: str = None,
|
|
75
|
+
max_sample_documents: int = 10,
|
|
76
|
+
**kwargs
|
|
77
|
+
):
|
|
78
|
+
"""
|
|
79
|
+
Initialize DocumentDB Agent.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
name: Agent name
|
|
83
|
+
host: DocumentDB hostname
|
|
84
|
+
port: DocumentDB port (default: 27017)
|
|
85
|
+
database: Database name
|
|
86
|
+
username: Username for authentication
|
|
87
|
+
password: Password for authentication
|
|
88
|
+
credentials: Credentials dict or connection string (overrides individual params)
|
|
89
|
+
ssl: Enable SSL/TLS connection (default: True)
|
|
90
|
+
tls_ca_file: Path to TLS CA certificate file
|
|
91
|
+
max_sample_documents: Maximum sample documents per collection
|
|
92
|
+
"""
|
|
93
|
+
self.host = host
|
|
94
|
+
self.port = port
|
|
95
|
+
self.database_name = database
|
|
96
|
+
self.username = username
|
|
97
|
+
self.password = password
|
|
98
|
+
self.ssl = ssl
|
|
99
|
+
self.tls_ca_file = tls_ca_file
|
|
100
|
+
self.max_sample_documents = max_sample_documents
|
|
101
|
+
self.db_connection: Optional[Any] = None
|
|
102
|
+
self.collections_cache: Dict[str, CollectionMetadata] = {}
|
|
103
|
+
|
|
104
|
+
# Get default credentials if not provided
|
|
105
|
+
if not credentials and not all([host, database]):
|
|
106
|
+
credentials = self._get_default_credentials()
|
|
107
|
+
|
|
108
|
+
super().__init__(
|
|
109
|
+
name=name,
|
|
110
|
+
credentials=credentials,
|
|
111
|
+
schema_name=database,
|
|
112
|
+
**kwargs
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Add DocumentDB-specific tools
|
|
116
|
+
self._setup_documentdb_tools()
|
|
117
|
+
|
|
118
|
+
def _get_default_credentials(self) -> Dict[str, Any]:
|
|
119
|
+
"""Get default credentials from config (similar to DatabaseQueryTool)."""
|
|
120
|
+
return {
|
|
121
|
+
'driver': 'mongo',
|
|
122
|
+
'host': config.get('DOCUMENTDB_HOSTNAME', fallback='localhost'),
|
|
123
|
+
'port': config.get('DOCUMENTDB_PORT', fallback=27017),
|
|
124
|
+
'database': config.get('DOCUMENTDB_DATABASE', fallback='test'),
|
|
125
|
+
'username': config.get('DOCUMENTDB_USERNAME'),
|
|
126
|
+
'password': config.get('DOCUMENTDB_PASSWORD'),
|
|
127
|
+
'tlsCAFile': config.get('DOCUMENTDB_TLS_CA_FILE') or BASE_DIR.joinpath('env', "global-bundle.pem"),
|
|
128
|
+
'ssl': config.get('DOCUMENTDB_USE_SSL', fallback=True),
|
|
129
|
+
'collection_name': config.get('DOCUMENTDB_COLLECTION', fallback='mycollection'),
|
|
130
|
+
'dbtype': 'documentdb'
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
def _setup_documentdb_tools(self):
|
|
134
|
+
"""Setup DocumentDB-specific tools."""
|
|
135
|
+
# Add collection query execution tool
|
|
136
|
+
collection_query_tool = CollectionQueryExecutionTool(agent=self)
|
|
137
|
+
self.tool_manager.register_tool(collection_query_tool)
|
|
138
|
+
|
|
139
|
+
# Add collection exploration tool
|
|
140
|
+
collection_exploration_tool = CollectionExplorationTool(agent=self)
|
|
141
|
+
self.tool_manager.register_tool(collection_exploration_tool)
|
|
142
|
+
|
|
143
|
+
async def connect_database(self) -> None:
|
|
144
|
+
"""Connect to DocumentDB using AsyncDB with mongo driver."""
|
|
145
|
+
# Parse credentials
|
|
146
|
+
if isinstance(self.credentials, dict):
|
|
147
|
+
creds = self.credentials.copy()
|
|
148
|
+
self.host = creds.get('host', self.host)
|
|
149
|
+
self.port = creds.get('port', self.port)
|
|
150
|
+
self.database_name = creds.get('database', self.database_name)
|
|
151
|
+
self.username = creds.get('username', self.username)
|
|
152
|
+
self.password = creds.get('password', self.password)
|
|
153
|
+
self.ssl = creds.get('ssl', self.ssl)
|
|
154
|
+
self.tls_ca_file = creds.get('tlsCAFile', self.tls_ca_file)
|
|
155
|
+
|
|
156
|
+
if not self.host:
|
|
157
|
+
raise ValueError("DocumentDB host is required")
|
|
158
|
+
if not self.database_name:
|
|
159
|
+
raise ValueError("DocumentDB database name is required")
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
# Build connection parameters for AsyncDB
|
|
163
|
+
params = {
|
|
164
|
+
'host': self.host,
|
|
165
|
+
'port': self.port,
|
|
166
|
+
'database': self.database_name,
|
|
167
|
+
'dbtype': 'documentdb'
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if self.username:
|
|
171
|
+
params['username'] = self.username
|
|
172
|
+
if self.password:
|
|
173
|
+
params['password'] = self.password
|
|
174
|
+
if self.ssl:
|
|
175
|
+
params['ssl'] = self.ssl
|
|
176
|
+
if self.tls_ca_file:
|
|
177
|
+
params['tlsCAFile'] = str(self.tls_ca_file)
|
|
178
|
+
|
|
179
|
+
# Create AsyncDB instance
|
|
180
|
+
self.db = AsyncDB('mongo', params=params)
|
|
181
|
+
|
|
182
|
+
# Test connection
|
|
183
|
+
async with await self.db.connection() as conn:
|
|
184
|
+
await conn.use(self.database_name)
|
|
185
|
+
# Simple ping to verify connection
|
|
186
|
+
result = await conn.execute({"ping": 1})
|
|
187
|
+
|
|
188
|
+
self.logger.info(
|
|
189
|
+
f"Successfully connected to DocumentDB at {self.host}:{self.port}"
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
except Exception as e:
|
|
193
|
+
self.logger.error(f"Failed to connect to DocumentDB: {e}")
|
|
194
|
+
raise
|
|
195
|
+
|
|
196
|
+
async def extract_schema_metadata(self) -> DatabaseSchema:
|
|
197
|
+
"""Extract schema metadata from DocumentDB (collections, fields, documents)."""
|
|
198
|
+
if not self.db:
|
|
199
|
+
await self.connect_database()
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
async with await self.db.connection() as conn:
|
|
203
|
+
await conn.use(self.database_name)
|
|
204
|
+
|
|
205
|
+
# Get list of collections
|
|
206
|
+
collections_result = await conn.execute({"listCollections": 1})
|
|
207
|
+
collection_names = [
|
|
208
|
+
col['name'] for col in collections_result.get('cursor', {}).get('firstBatch', [])
|
|
209
|
+
if not col['name'].startswith('system.')
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
# Extract metadata for each collection
|
|
213
|
+
all_collections = []
|
|
214
|
+
for collection_name in collection_names:
|
|
215
|
+
collection_metadata = await self._extract_collection_metadata(
|
|
216
|
+
conn, collection_name
|
|
217
|
+
)
|
|
218
|
+
all_collections.append(collection_metadata)
|
|
219
|
+
|
|
220
|
+
# Cache for later use
|
|
221
|
+
cache_key = f"{self.database_name}.{collection_name}"
|
|
222
|
+
self.collections_cache[cache_key] = collection_metadata
|
|
223
|
+
|
|
224
|
+
# Convert collections to TableMetadata format
|
|
225
|
+
tables = self._convert_collections_to_tables(all_collections)
|
|
226
|
+
|
|
227
|
+
schema_metadata = DatabaseSchema(
|
|
228
|
+
database_name=self.database_name,
|
|
229
|
+
database_type="documentdb",
|
|
230
|
+
tables=tables,
|
|
231
|
+
views=[], # DocumentDB doesn't have traditional views
|
|
232
|
+
functions=[],
|
|
233
|
+
procedures=[],
|
|
234
|
+
metadata={
|
|
235
|
+
"collections_analyzed": len(all_collections),
|
|
236
|
+
"extraction_timestamp": datetime.now().isoformat(),
|
|
237
|
+
"host": self.host,
|
|
238
|
+
"port": self.port
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
self.logger.info(
|
|
243
|
+
f"Extracted metadata for {len(all_collections)} collections from {self.database_name}"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
return schema_metadata
|
|
247
|
+
|
|
248
|
+
except Exception as e:
|
|
249
|
+
self.logger.error(f"Failed to extract DocumentDB schema metadata: {e}")
|
|
250
|
+
raise
|
|
251
|
+
|
|
252
|
+
async def _extract_collection_metadata(
|
|
253
|
+
self,
|
|
254
|
+
conn: Any,
|
|
255
|
+
collection_name: str
|
|
256
|
+
) -> CollectionMetadata:
|
|
257
|
+
"""Extract detailed metadata for a specific collection."""
|
|
258
|
+
try:
|
|
259
|
+
# Get document count
|
|
260
|
+
count_result = await conn.count(collection_name, {})
|
|
261
|
+
document_count = count_result if isinstance(count_result, int) else 0
|
|
262
|
+
|
|
263
|
+
# Get sample documents to infer schema
|
|
264
|
+
sample_docs = await conn.fetch(
|
|
265
|
+
collection_name,
|
|
266
|
+
{},
|
|
267
|
+
limit=self.max_sample_documents
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Infer fields from sample documents
|
|
271
|
+
fields = self._infer_fields_from_documents(sample_docs)
|
|
272
|
+
|
|
273
|
+
# Get indexes
|
|
274
|
+
indexes = []
|
|
275
|
+
try:
|
|
276
|
+
indexes_cursor = await conn.execute({
|
|
277
|
+
"listIndexes": collection_name
|
|
278
|
+
})
|
|
279
|
+
indexes = list(indexes_cursor.get('cursor', {}).get('firstBatch', []))
|
|
280
|
+
except Exception as e:
|
|
281
|
+
self.logger.warning(f"Could not get indexes for {collection_name}: {e}")
|
|
282
|
+
|
|
283
|
+
return CollectionMetadata(
|
|
284
|
+
name=collection_name,
|
|
285
|
+
database=self.database_name,
|
|
286
|
+
document_count=document_count,
|
|
287
|
+
fields=fields,
|
|
288
|
+
sample_documents=sample_docs[:5] if sample_docs else [],
|
|
289
|
+
indexes=indexes
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
except Exception as e:
|
|
293
|
+
self.logger.warning(
|
|
294
|
+
f"Could not extract metadata for collection {collection_name}: {e}"
|
|
295
|
+
)
|
|
296
|
+
return CollectionMetadata(
|
|
297
|
+
name=collection_name,
|
|
298
|
+
database=self.database_name,
|
|
299
|
+
document_count=0,
|
|
300
|
+
fields=[],
|
|
301
|
+
sample_documents=[],
|
|
302
|
+
indexes=[]
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
def _infer_fields_from_documents(
|
|
306
|
+
self,
|
|
307
|
+
documents: List[Dict[str, Any]]
|
|
308
|
+
) -> List[Dict[str, str]]:
|
|
309
|
+
"""Infer field schema from sample documents."""
|
|
310
|
+
if not documents:
|
|
311
|
+
return []
|
|
312
|
+
|
|
313
|
+
# Collect all fields and their types
|
|
314
|
+
field_types = {}
|
|
315
|
+
for doc in documents:
|
|
316
|
+
for key, value in doc.items():
|
|
317
|
+
if key not in field_types:
|
|
318
|
+
field_types[key] = set()
|
|
319
|
+
field_types[key].add(type(value).__name__)
|
|
320
|
+
|
|
321
|
+
# Create field list
|
|
322
|
+
fields = []
|
|
323
|
+
for field_name, types in sorted(field_types.items()):
|
|
324
|
+
type_str = ' | '.join(sorted(types))
|
|
325
|
+
fields.append({
|
|
326
|
+
"name": field_name,
|
|
327
|
+
"type": type_str
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
return fields
|
|
331
|
+
|
|
332
|
+
def _convert_collections_to_tables(
|
|
333
|
+
self,
|
|
334
|
+
collections: List[CollectionMetadata]
|
|
335
|
+
) -> List[TableMetadata]:
|
|
336
|
+
"""Convert DocumentDB collections to TableMetadata format."""
|
|
337
|
+
tables = []
|
|
338
|
+
|
|
339
|
+
for collection in collections:
|
|
340
|
+
# Create columns list from fields
|
|
341
|
+
columns = []
|
|
342
|
+
for field in collection.fields:
|
|
343
|
+
columns.append({
|
|
344
|
+
"name": field["name"],
|
|
345
|
+
"type": field["type"],
|
|
346
|
+
"nullable": True, # MongoDB fields are generally nullable
|
|
347
|
+
"description": f"Field: {field['name']}"
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
# Create table metadata
|
|
351
|
+
table_metadata = TableMetadata(
|
|
352
|
+
name=collection.name,
|
|
353
|
+
schema=self.database_name,
|
|
354
|
+
columns=columns,
|
|
355
|
+
primary_keys=["_id"], # MongoDB always has _id
|
|
356
|
+
foreign_keys=[], # DocumentDB doesn't enforce foreign keys
|
|
357
|
+
indexes=[idx.get('name', '') for idx in collection.indexes],
|
|
358
|
+
description=f"DocumentDB collection with {collection.document_count} documents",
|
|
359
|
+
sample_data=collection.sample_documents
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
tables.append(table_metadata)
|
|
363
|
+
|
|
364
|
+
return tables
|
|
365
|
+
|
|
366
|
+
async def generate_query(
|
|
367
|
+
self,
|
|
368
|
+
natural_language_query: str,
|
|
369
|
+
target_tables: Optional[List[str]] = None,
|
|
370
|
+
query_type: str = "find"
|
|
371
|
+
) -> Dict[str, Any]:
|
|
372
|
+
"""Generate MongoDB query from natural language."""
|
|
373
|
+
try:
|
|
374
|
+
# Get schema context for the query
|
|
375
|
+
schema_context = []
|
|
376
|
+
if self.schema_metadata:
|
|
377
|
+
# Filter collections based on target_tables
|
|
378
|
+
collections_to_use = self.schema_metadata.tables
|
|
379
|
+
if target_tables:
|
|
380
|
+
collections_to_use = [
|
|
381
|
+
t for t in collections_to_use if t.name in target_tables
|
|
382
|
+
]
|
|
383
|
+
|
|
384
|
+
for table in collections_to_use[:3]: # Limit to top 3
|
|
385
|
+
schema_context.append({
|
|
386
|
+
'collection': table.name,
|
|
387
|
+
'fields': [col['name'] for col in table.columns],
|
|
388
|
+
'sample_count': len(table.sample_data)
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
# Build MQL query generation prompt
|
|
392
|
+
prompt = self._build_mql_query_prompt(
|
|
393
|
+
natural_language_query=natural_language_query,
|
|
394
|
+
schema_context=schema_context
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
# Generate query using LLM
|
|
398
|
+
async with self._llm as client:
|
|
399
|
+
response = await client.ask(
|
|
400
|
+
prompt=prompt,
|
|
401
|
+
model=self._llm_model,
|
|
402
|
+
temperature=0.1
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# Extract MQL query from response
|
|
406
|
+
mql_query = self._extract_mql_from_response(response.output or response.response)
|
|
407
|
+
|
|
408
|
+
result = {
|
|
409
|
+
"query": mql_query,
|
|
410
|
+
"query_type": "mql",
|
|
411
|
+
"collections_used": target_tables or [],
|
|
412
|
+
"schema_context_used": len(schema_context),
|
|
413
|
+
"natural_language_input": natural_language_query
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
return result
|
|
417
|
+
|
|
418
|
+
except Exception as e:
|
|
419
|
+
self.logger.error(f"Failed to generate MQL query: {e}")
|
|
420
|
+
raise
|
|
421
|
+
|
|
422
|
+
def _build_mql_query_prompt(
|
|
423
|
+
self,
|
|
424
|
+
natural_language_query: str,
|
|
425
|
+
schema_context: List[Dict[str, Any]]
|
|
426
|
+
) -> str:
|
|
427
|
+
"""Build prompt for MQL query generation."""
|
|
428
|
+
prompt = f"""
|
|
429
|
+
You are an expert MongoDB/DocumentDB query developer.
|
|
430
|
+
Generate a MongoDB query based on the natural language request and the provided schema information.
|
|
431
|
+
|
|
432
|
+
Natural Language Request: {natural_language_query}
|
|
433
|
+
|
|
434
|
+
Available Collections and Schema:
|
|
435
|
+
"""
|
|
436
|
+
for i, context in enumerate(schema_context, 1):
|
|
437
|
+
prompt += f"\n{i}. Collection: {context['collection']}\n"
|
|
438
|
+
prompt += f" Fields: {', '.join(context['fields'])}\n"
|
|
439
|
+
|
|
440
|
+
prompt += """
|
|
441
|
+
|
|
442
|
+
MongoDB Query Guidelines:
|
|
443
|
+
1. Use standard MongoDB query syntax
|
|
444
|
+
2. Return query as a JSON object
|
|
445
|
+
3. Use the find command structure: {"find": "collection_name", "filter": {...}, "limit": N}
|
|
446
|
+
4. Common operators: $eq, $ne, $gt, $lt, $gte, $lte, $in, $nin, $and, $or, $not
|
|
447
|
+
5. For text search use $regex or $text
|
|
448
|
+
6. Include projection to limit returned fields if needed
|
|
449
|
+
7. Return only the MongoDB query as valid JSON without explanations
|
|
450
|
+
|
|
451
|
+
Example query structure:
|
|
452
|
+
{
|
|
453
|
+
"find": "users",
|
|
454
|
+
"filter": {"age": {"$gte": 18}, "status": "active"},
|
|
455
|
+
"limit": 10,
|
|
456
|
+
"sort": {"created_at": -1}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
MongoDB Query (JSON only):"""
|
|
460
|
+
|
|
461
|
+
return prompt
|
|
462
|
+
|
|
463
|
+
def _extract_mql_from_response(self, response_text: str) -> Dict[str, Any]:
|
|
464
|
+
"""Extract MQL query from LLM response."""
|
|
465
|
+
# Remove markdown code blocks if present
|
|
466
|
+
if "```" in response_text:
|
|
467
|
+
lines = response_text.split('\n')
|
|
468
|
+
json_lines = []
|
|
469
|
+
in_code_block = False
|
|
470
|
+
|
|
471
|
+
for line in lines:
|
|
472
|
+
if line.strip().startswith("```"):
|
|
473
|
+
in_code_block = not in_code_block
|
|
474
|
+
continue
|
|
475
|
+
elif in_code_block or (line.strip().startswith('{') or json_lines):
|
|
476
|
+
json_lines.append(line)
|
|
477
|
+
|
|
478
|
+
response_text = '\n'.join(json_lines).strip()
|
|
479
|
+
|
|
480
|
+
# Parse JSON
|
|
481
|
+
try:
|
|
482
|
+
return json.loads(response_text)
|
|
483
|
+
except json.JSONDecodeError:
|
|
484
|
+
# Try to extract first JSON object
|
|
485
|
+
import re
|
|
486
|
+
match = re.search(r'\{.*\}', response_text, re.DOTALL)
|
|
487
|
+
if match:
|
|
488
|
+
return json.loads(match.group())
|
|
489
|
+
else:
|
|
490
|
+
return {"filter": {}}
|
|
491
|
+
|
|
492
|
+
async def execute_query(
|
|
493
|
+
self,
|
|
494
|
+
query: Union[str, Dict[str, Any]],
|
|
495
|
+
limit: Optional[int] = 100
|
|
496
|
+
) -> Dict[str, Any]:
|
|
497
|
+
"""Execute MongoDB query against DocumentDB."""
|
|
498
|
+
try:
|
|
499
|
+
if not self.db:
|
|
500
|
+
await self.connect_database()
|
|
501
|
+
|
|
502
|
+
# Parse query if string
|
|
503
|
+
if isinstance(query, str):
|
|
504
|
+
query = json.loads(query)
|
|
505
|
+
|
|
506
|
+
async with await self.db.connection() as conn:
|
|
507
|
+
await conn.use(self.database_name)
|
|
508
|
+
|
|
509
|
+
# Extract query components
|
|
510
|
+
collection_name = query.get('find') or query.get('collection')
|
|
511
|
+
filter_query = query.get('filter', {})
|
|
512
|
+
query_limit = query.get('limit', limit)
|
|
513
|
+
projection = query.get('projection')
|
|
514
|
+
sort = query.get('sort')
|
|
515
|
+
|
|
516
|
+
if not collection_name:
|
|
517
|
+
raise ValueError("Query must specify a collection name")
|
|
518
|
+
|
|
519
|
+
# Execute query
|
|
520
|
+
results = await conn.fetch(
|
|
521
|
+
collection_name,
|
|
522
|
+
filter_query,
|
|
523
|
+
limit=query_limit
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
return {
|
|
527
|
+
"success": True,
|
|
528
|
+
"data": results,
|
|
529
|
+
"record_count": len(results),
|
|
530
|
+
"query": query,
|
|
531
|
+
"collection": collection_name
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
except Exception as e:
|
|
535
|
+
self.logger.error(f"DocumentDB query execution failed: {e}")
|
|
536
|
+
return {
|
|
537
|
+
"success": False,
|
|
538
|
+
"error": str(e),
|
|
539
|
+
"query": query
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
async def close(self):
|
|
543
|
+
"""Close DocumentDB connection."""
|
|
544
|
+
if self.db:
|
|
545
|
+
await self.db.close()
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
class CollectionQueryExecutionTool(AbstractTool):
|
|
549
|
+
"""Tool for executing queries against DocumentDB collections."""
|
|
550
|
+
|
|
551
|
+
name = "execute_documentdb_query"
|
|
552
|
+
description = "Execute MongoDB queries against the DocumentDB database"
|
|
553
|
+
args_schema = CollectionQueryExecutionArgs
|
|
554
|
+
|
|
555
|
+
def __init__(self, agent: DocumentDBAgent, **kwargs):
|
|
556
|
+
super().__init__(**kwargs)
|
|
557
|
+
self.agent = agent
|
|
558
|
+
|
|
559
|
+
async def _execute(
|
|
560
|
+
self,
|
|
561
|
+
collection_name: str,
|
|
562
|
+
query: Dict[str, Any] = None,
|
|
563
|
+
limit: Optional[int] = 100,
|
|
564
|
+
projection: Optional[Dict[str, int]] = None
|
|
565
|
+
) -> ToolResult:
|
|
566
|
+
"""Execute collection query."""
|
|
567
|
+
try:
|
|
568
|
+
# Build query object
|
|
569
|
+
full_query = {
|
|
570
|
+
"find": collection_name,
|
|
571
|
+
"filter": query or {},
|
|
572
|
+
"limit": limit
|
|
573
|
+
}
|
|
574
|
+
if projection:
|
|
575
|
+
full_query["projection"] = projection
|
|
576
|
+
|
|
577
|
+
result = await self.agent.execute_query(full_query, limit)
|
|
578
|
+
|
|
579
|
+
return ToolResult(
|
|
580
|
+
status="success" if result["success"] else "error",
|
|
581
|
+
result=result,
|
|
582
|
+
error=result.get("error"),
|
|
583
|
+
metadata={
|
|
584
|
+
"collection": collection_name,
|
|
585
|
+
"limit": limit
|
|
586
|
+
}
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
except Exception as e:
|
|
590
|
+
return ToolResult(
|
|
591
|
+
status="error",
|
|
592
|
+
result=None,
|
|
593
|
+
error=str(e),
|
|
594
|
+
metadata={"collection": collection_name}
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
class CollectionExplorationTool(AbstractTool):
|
|
599
|
+
"""Tool for exploring DocumentDB collections and their metadata."""
|
|
600
|
+
|
|
601
|
+
name = "explore_collections"
|
|
602
|
+
description = "Explore available collections and fields in DocumentDB"
|
|
603
|
+
|
|
604
|
+
class ExplorationArgs(AbstractToolArgsSchema):
|
|
605
|
+
"""Exploration arguments schema."""
|
|
606
|
+
collection: Optional[str] = Field(
|
|
607
|
+
default=None, description="Specific collection to explore"
|
|
608
|
+
)
|
|
609
|
+
show_sample_data: bool = Field(
|
|
610
|
+
default=True, description="Include sample data in results"
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
args_schema = ExplorationArgs
|
|
614
|
+
|
|
615
|
+
def __init__(self, agent: DocumentDBAgent, **kwargs):
|
|
616
|
+
super().__init__(**kwargs)
|
|
617
|
+
self.agent = agent
|
|
618
|
+
|
|
619
|
+
async def _execute(
|
|
620
|
+
self,
|
|
621
|
+
collection: Optional[str] = None,
|
|
622
|
+
show_sample_data: bool = True
|
|
623
|
+
) -> ToolResult:
|
|
624
|
+
"""Explore collections in DocumentDB."""
|
|
625
|
+
try:
|
|
626
|
+
if not self.agent.schema_metadata:
|
|
627
|
+
await self.agent.extract_schema_metadata()
|
|
628
|
+
|
|
629
|
+
exploration_result = {
|
|
630
|
+
"collections": [],
|
|
631
|
+
"total_collections": 0
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
# Filter by collection if specified
|
|
635
|
+
tables_to_explore = self.agent.schema_metadata.tables
|
|
636
|
+
if collection:
|
|
637
|
+
tables_to_explore = [t for t in tables_to_explore if t.name == collection]
|
|
638
|
+
|
|
639
|
+
for table in tables_to_explore:
|
|
640
|
+
collection_info = {
|
|
641
|
+
"name": table.name,
|
|
642
|
+
"document_count": table.description,
|
|
643
|
+
"fields": [col['name'] for col in table.columns],
|
|
644
|
+
"indexes": table.indexes
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if show_sample_data and table.sample_data:
|
|
648
|
+
collection_info["sample_documents"] = table.sample_data[:3]
|
|
649
|
+
|
|
650
|
+
exploration_result["collections"].append(collection_info)
|
|
651
|
+
|
|
652
|
+
exploration_result["total_collections"] = len(exploration_result["collections"])
|
|
653
|
+
|
|
654
|
+
return ToolResult(
|
|
655
|
+
status="success",
|
|
656
|
+
result=exploration_result,
|
|
657
|
+
metadata={
|
|
658
|
+
"database": self.agent.database_name,
|
|
659
|
+
"collection_filter": collection
|
|
660
|
+
}
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
except Exception as e:
|
|
664
|
+
return ToolResult(
|
|
665
|
+
status="error",
|
|
666
|
+
result=None,
|
|
667
|
+
error=str(e)
|
|
668
|
+
)
|