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,68 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import List, Dict, Any, Tuple
|
|
3
|
+
from navconfig.logging import logging
|
|
4
|
+
from ...utils.helpers import RequestContext
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AbstractKnowledgeBase(ABC):
|
|
8
|
+
"""Base class for all knowledge bases."""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
name: str,
|
|
13
|
+
category: str,
|
|
14
|
+
description: str = None,
|
|
15
|
+
activation_patterns: List[str] = None,
|
|
16
|
+
always_active: bool = False,
|
|
17
|
+
priority: int = 0
|
|
18
|
+
):
|
|
19
|
+
self.name = name
|
|
20
|
+
self.category = category
|
|
21
|
+
self.activation_patterns = activation_patterns or []
|
|
22
|
+
self.description = description or f"{name} knowledge base"
|
|
23
|
+
self.always_active = always_active
|
|
24
|
+
self.priority = priority # Higher = included first
|
|
25
|
+
self.logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
async def should_activate(
|
|
29
|
+
self,
|
|
30
|
+
query: str,
|
|
31
|
+
context: Dict[str, Any]
|
|
32
|
+
) -> Tuple[bool, float]:
|
|
33
|
+
"""
|
|
34
|
+
Determine if this KB should be activated for the query.
|
|
35
|
+
Returns (should_activate, confidence_score)
|
|
36
|
+
"""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
async def close(self):
|
|
40
|
+
"""Cleanup resources if needed."""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
@abstractmethod
|
|
44
|
+
async def search(
|
|
45
|
+
self,
|
|
46
|
+
query: str,
|
|
47
|
+
k: int = 5,
|
|
48
|
+
score_threshold: float = 0.6,
|
|
49
|
+
user_id: str = None,
|
|
50
|
+
session_id: str = None,
|
|
51
|
+
ctx: RequestContext = None,
|
|
52
|
+
**kwargs
|
|
53
|
+
) -> List[Dict[str, Any]]:
|
|
54
|
+
"""
|
|
55
|
+
Search for relevant facts/information.
|
|
56
|
+
Different KBs implement this differently.
|
|
57
|
+
"""
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
def format_context(self, results: List[Dict]) -> str:
|
|
61
|
+
"""Format results for prompt injection."""
|
|
62
|
+
if not results:
|
|
63
|
+
return ""
|
|
64
|
+
|
|
65
|
+
lines = [f"## {self.name}:"]
|
|
66
|
+
for result in results:
|
|
67
|
+
lines.append(f"* {result.get('content', result)}")
|
|
68
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import hashlib
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, Optional, Callable
|
|
5
|
+
from datetime import datetime, timedelta
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class CacheEntry:
|
|
10
|
+
"""Cache entry with TTL support."""
|
|
11
|
+
value: Any
|
|
12
|
+
expires_at: datetime
|
|
13
|
+
hit_count: int = 0
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def is_expired(self) -> bool:
|
|
17
|
+
return datetime.now() > self.expires_at
|
|
18
|
+
|
|
19
|
+
def accessed(self):
|
|
20
|
+
"""Track cache hits for analytics."""
|
|
21
|
+
self.hit_count += 1
|
|
22
|
+
|
|
23
|
+
class TTLCache:
|
|
24
|
+
"""Thread-safe TTL cache with memory management."""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
max_size: int = 1000,
|
|
29
|
+
default_ttl: int = 300, # 5 minutes default
|
|
30
|
+
cleanup_interval: int = 60 # Run cleanup every minute
|
|
31
|
+
):
|
|
32
|
+
self._cache: Dict[str, CacheEntry] = {}
|
|
33
|
+
self._max_size = max_size
|
|
34
|
+
self._default_ttl = default_ttl
|
|
35
|
+
self._lock = asyncio.Lock()
|
|
36
|
+
self._cleanup_task = None
|
|
37
|
+
self._stats = {
|
|
38
|
+
'hits': 0,
|
|
39
|
+
'misses': 0,
|
|
40
|
+
'evictions': 0
|
|
41
|
+
}
|
|
42
|
+
self.cleanup_interval = cleanup_interval
|
|
43
|
+
|
|
44
|
+
async def start(self):
|
|
45
|
+
"""Start the cleanup background task."""
|
|
46
|
+
if not self._cleanup_task:
|
|
47
|
+
self._cleanup_task = asyncio.create_task(
|
|
48
|
+
self._periodic_cleanup(self.cleanup_interval)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
async def stop(self):
|
|
52
|
+
"""Stop the cleanup task."""
|
|
53
|
+
if self._cleanup_task:
|
|
54
|
+
self._cleanup_task.cancel()
|
|
55
|
+
try:
|
|
56
|
+
await self._cleanup_task
|
|
57
|
+
except asyncio.CancelledError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
def _generate_key(self, *args, **kwargs) -> str:
|
|
61
|
+
"""Generate cache key from arguments."""
|
|
62
|
+
key_data = {
|
|
63
|
+
'args': args,
|
|
64
|
+
'kwargs': sorted(kwargs.items())
|
|
65
|
+
}
|
|
66
|
+
key_str = json.dumps(key_data, sort_keys=True, default=str)
|
|
67
|
+
return hashlib.md5(key_str.encode()).hexdigest()
|
|
68
|
+
|
|
69
|
+
async def get(
|
|
70
|
+
self,
|
|
71
|
+
key: str,
|
|
72
|
+
default: Any = None
|
|
73
|
+
) -> Optional[Any]:
|
|
74
|
+
"""Get value from cache."""
|
|
75
|
+
async with self._lock:
|
|
76
|
+
entry = self._cache.get(key)
|
|
77
|
+
|
|
78
|
+
if entry is None:
|
|
79
|
+
self._stats['misses'] += 1
|
|
80
|
+
return default
|
|
81
|
+
|
|
82
|
+
if entry.is_expired:
|
|
83
|
+
del self._cache[key]
|
|
84
|
+
self._stats['misses'] += 1
|
|
85
|
+
return default
|
|
86
|
+
|
|
87
|
+
entry.accessed()
|
|
88
|
+
self._stats['hits'] += 1
|
|
89
|
+
return entry.value
|
|
90
|
+
|
|
91
|
+
async def set(
|
|
92
|
+
self,
|
|
93
|
+
key: str,
|
|
94
|
+
value: Any,
|
|
95
|
+
ttl: Optional[int] = None
|
|
96
|
+
):
|
|
97
|
+
"""Set value in cache with TTL."""
|
|
98
|
+
ttl = ttl or self._default_ttl
|
|
99
|
+
expires_at = datetime.now() + timedelta(seconds=ttl)
|
|
100
|
+
|
|
101
|
+
async with self._lock:
|
|
102
|
+
# Check size limit
|
|
103
|
+
if len(self._cache) >= self._max_size:
|
|
104
|
+
await self._evict_lru()
|
|
105
|
+
|
|
106
|
+
self._cache[key] = CacheEntry(
|
|
107
|
+
value=value,
|
|
108
|
+
expires_at=expires_at
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
async def invalidate(self, pattern: str = None):
|
|
112
|
+
"""Invalidate cache entries matching pattern."""
|
|
113
|
+
async with self._lock:
|
|
114
|
+
if pattern:
|
|
115
|
+
keys_to_remove = [
|
|
116
|
+
k for k in self._cache.keys()
|
|
117
|
+
if pattern in k
|
|
118
|
+
]
|
|
119
|
+
for key in keys_to_remove:
|
|
120
|
+
del self._cache[key]
|
|
121
|
+
else:
|
|
122
|
+
self._cache.clear()
|
|
123
|
+
|
|
124
|
+
async def _evict_lru(self):
|
|
125
|
+
"""Evict least recently used entries."""
|
|
126
|
+
if not self._cache:
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
# Find oldest entry
|
|
130
|
+
oldest_key = min(
|
|
131
|
+
self._cache.keys(),
|
|
132
|
+
key=lambda k: self._cache[k].expires_at
|
|
133
|
+
)
|
|
134
|
+
del self._cache[oldest_key]
|
|
135
|
+
self._stats['evictions'] += 1
|
|
136
|
+
|
|
137
|
+
async def _periodic_cleanup(self, interval: int):
|
|
138
|
+
"""Periodically clean up expired entries."""
|
|
139
|
+
while True:
|
|
140
|
+
try:
|
|
141
|
+
await asyncio.sleep(interval)
|
|
142
|
+
await self._cleanup_expired()
|
|
143
|
+
except asyncio.CancelledError:
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
async def _cleanup_expired(self):
|
|
147
|
+
"""Remove expired entries."""
|
|
148
|
+
async with self._lock:
|
|
149
|
+
expired_keys = [
|
|
150
|
+
k for k, v in self._cache.items()
|
|
151
|
+
if v.is_expired
|
|
152
|
+
]
|
|
153
|
+
for key in expired_keys:
|
|
154
|
+
del self._cache[key]
|
|
155
|
+
|
|
156
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
157
|
+
"""Get cache statistics."""
|
|
158
|
+
total = self._stats['hits'] + self._stats['misses']
|
|
159
|
+
hit_rate = (self._stats['hits'] / total * 100) if total > 0 else 0
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
**self._stats,
|
|
163
|
+
'size': len(self._cache),
|
|
164
|
+
'hit_rate': f"{hit_rate:.2f}%"
|
|
165
|
+
}
|
parrot/stores/kb/doc.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional
|
|
2
|
+
from .redis import RedisKnowledgeBase
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class UserContext(RedisKnowledgeBase):
|
|
6
|
+
"""Knowledge Base for user context and session data."""
|
|
7
|
+
|
|
8
|
+
def __init__(self, **kwargs):
|
|
9
|
+
super().__init__(
|
|
10
|
+
name="User Context",
|
|
11
|
+
category="context",
|
|
12
|
+
namespace="user_context",
|
|
13
|
+
activation_patterns=[
|
|
14
|
+
"last conversation", "previously", "we discussed",
|
|
15
|
+
"earlier you mentioned", "remember when"
|
|
16
|
+
],
|
|
17
|
+
use_hash_storage=True,
|
|
18
|
+
ttl=86400 * 30, # 30 days expiration
|
|
19
|
+
**kwargs
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
async def search(
|
|
23
|
+
self,
|
|
24
|
+
query: str,
|
|
25
|
+
user_id: Optional[str] = None,
|
|
26
|
+
**kwargs
|
|
27
|
+
) -> List[Dict[str, Any]]:
|
|
28
|
+
"""Retrieve user context matching the query."""
|
|
29
|
+
if not user_id:
|
|
30
|
+
return []
|
|
31
|
+
|
|
32
|
+
context = await self.get(user_id)
|
|
33
|
+
if not context:
|
|
34
|
+
return []
|
|
35
|
+
|
|
36
|
+
facts = []
|
|
37
|
+
query_lower = query.lower()
|
|
38
|
+
|
|
39
|
+
# Search through context fields
|
|
40
|
+
for key, value in context.items():
|
|
41
|
+
if query_lower in key.lower() or query_lower in str(value).lower():
|
|
42
|
+
facts.append({
|
|
43
|
+
'content': f"User context - {key}: {value}",
|
|
44
|
+
'metadata': {
|
|
45
|
+
'context_key': key,
|
|
46
|
+
'user_id': user_id,
|
|
47
|
+
'value': value
|
|
48
|
+
},
|
|
49
|
+
'source': 'user_context',
|
|
50
|
+
'relevance': 1.0 if query_lower == key.lower() else 0.5
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return facts
|
|
54
|
+
|
|
55
|
+
async def update_context(
|
|
56
|
+
self,
|
|
57
|
+
user_id: str,
|
|
58
|
+
context_data: Dict[str, Any]
|
|
59
|
+
) -> bool:
|
|
60
|
+
"""
|
|
61
|
+
Update user context with new data.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
user_id: User identifier
|
|
65
|
+
context_data: Context data to update
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
True if successful
|
|
69
|
+
"""
|
|
70
|
+
return await self.update(user_id, context_data)
|
|
71
|
+
|
|
72
|
+
async def get_context(self, user_id: str) -> Dict[str, Any]:
|
|
73
|
+
"""Get all context for a user."""
|
|
74
|
+
return await self.get(user_id, default={})
|
|
75
|
+
|
|
76
|
+
async def set_context_field(
|
|
77
|
+
self,
|
|
78
|
+
user_id: str,
|
|
79
|
+
field: str,
|
|
80
|
+
value: Any
|
|
81
|
+
) -> bool:
|
|
82
|
+
"""Set a specific context field."""
|
|
83
|
+
return await self.insert(user_id, value, field=field)
|
|
84
|
+
|
|
85
|
+
async def get_context_field(
|
|
86
|
+
self,
|
|
87
|
+
user_id: str,
|
|
88
|
+
field: str,
|
|
89
|
+
default: Any = None
|
|
90
|
+
) -> Any:
|
|
91
|
+
"""Get a specific context field."""
|
|
92
|
+
return await self.get(user_id, field=field, default=default)
|
|
93
|
+
|
|
94
|
+
async def clear_context(self, user_id: str) -> bool:
|
|
95
|
+
"""Clear all context for a user."""
|
|
96
|
+
return await self.delete(user_id)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ChatbotSettings(RedisKnowledgeBase):
|
|
100
|
+
"""Knowledge Base for chatbot-specific settings."""
|
|
101
|
+
|
|
102
|
+
def __init__(self, **kwargs):
|
|
103
|
+
super().__init__(
|
|
104
|
+
name="Chatbot Settings",
|
|
105
|
+
category="settings",
|
|
106
|
+
namespace="bot_settings",
|
|
107
|
+
activation_patterns=[], # Not activated by patterns
|
|
108
|
+
use_hash_storage=True,
|
|
109
|
+
**kwargs
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
async def search(
|
|
113
|
+
self,
|
|
114
|
+
query: str,
|
|
115
|
+
chatbot_id: Optional[str] = None,
|
|
116
|
+
**kwargs
|
|
117
|
+
) -> List[Dict[str, Any]]:
|
|
118
|
+
"""Retrieve chatbot settings."""
|
|
119
|
+
if not chatbot_id:
|
|
120
|
+
return []
|
|
121
|
+
|
|
122
|
+
settings = await self.get(chatbot_id)
|
|
123
|
+
if not settings:
|
|
124
|
+
return []
|
|
125
|
+
|
|
126
|
+
return [{
|
|
127
|
+
'content': f"Chatbot settings: {settings}",
|
|
128
|
+
'metadata': {
|
|
129
|
+
'chatbot_id': chatbot_id,
|
|
130
|
+
'settings': settings
|
|
131
|
+
},
|
|
132
|
+
'source': 'chatbot_settings'
|
|
133
|
+
}]
|
|
134
|
+
|
|
135
|
+
async def get_setting(
|
|
136
|
+
self,
|
|
137
|
+
chatbot_id: str,
|
|
138
|
+
setting: str,
|
|
139
|
+
default: Any = None
|
|
140
|
+
) -> Any:
|
|
141
|
+
"""Get a specific chatbot setting."""
|
|
142
|
+
return await self.get(chatbot_id, field=setting, default=default)
|
|
143
|
+
|
|
144
|
+
async def set_setting(
|
|
145
|
+
self,
|
|
146
|
+
chatbot_id: str,
|
|
147
|
+
setting: str,
|
|
148
|
+
value: Any
|
|
149
|
+
) -> bool:
|
|
150
|
+
"""Set a specific chatbot setting."""
|
|
151
|
+
return await self.insert(chatbot_id, value, field=setting)
|
|
152
|
+
|
|
153
|
+
class DocumentMetadata(RedisKnowledgeBase):
|
|
154
|
+
"""Knowledge Base for document metadata and indexing."""
|
|
155
|
+
|
|
156
|
+
def __init__(self, **kwargs):
|
|
157
|
+
super().__init__(
|
|
158
|
+
name="Document Metadata",
|
|
159
|
+
category="documents",
|
|
160
|
+
namespace="doc_meta",
|
|
161
|
+
activation_patterns=[
|
|
162
|
+
"document", "file", "uploaded", "attachment"
|
|
163
|
+
],
|
|
164
|
+
use_hash_storage=True,
|
|
165
|
+
**kwargs
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
async def search(
|
|
169
|
+
self,
|
|
170
|
+
query: str,
|
|
171
|
+
user_id: Optional[str] = None,
|
|
172
|
+
**kwargs
|
|
173
|
+
) -> List[Dict[str, Any]]:
|
|
174
|
+
"""Search document metadata.
|
|
175
|
+
|
|
176
|
+
FIXED: Properly handle user_id filtering since it's stored as a field,
|
|
177
|
+
not part of the key structure.
|
|
178
|
+
"""
|
|
179
|
+
query_lower = query.lower()
|
|
180
|
+
results = []
|
|
181
|
+
|
|
182
|
+
# Search across ALL documents (don't pass user_id to base search)
|
|
183
|
+
# The base search would try to use it in the key pattern which is wrong
|
|
184
|
+
all_results = await super().search(
|
|
185
|
+
query,
|
|
186
|
+
identifier=None, # Search all documents
|
|
187
|
+
field_filter=['title', 'description', 'tags', 'filename'],
|
|
188
|
+
limit=kwargs.get('limit', 100)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Now filter by user_id if provided
|
|
192
|
+
for result in all_results:
|
|
193
|
+
doc_data = result.get('data', {})
|
|
194
|
+
|
|
195
|
+
# Filter by user_id if specified
|
|
196
|
+
if user_id and doc_data.get('user_id') != user_id:
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
# Format results for document context
|
|
200
|
+
results.append({
|
|
201
|
+
'content': f"Document: {doc_data.get('title', 'Unknown')} - {doc_data.get('description', '')}",
|
|
202
|
+
'metadata': {
|
|
203
|
+
'document_id': result.get('identifier'),
|
|
204
|
+
'user_id': doc_data.get('user_id'),
|
|
205
|
+
'title': doc_data.get('title'),
|
|
206
|
+
'filename': doc_data.get('filename'),
|
|
207
|
+
'tags': doc_data.get('tags', []),
|
|
208
|
+
**{k: v for k, v in doc_data.items() if k not in ['title', 'filename', 'tags', 'user_id']}
|
|
209
|
+
},
|
|
210
|
+
'source': 'document_metadata',
|
|
211
|
+
'relevance': result.get('relevance', 0.0)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
# Sort by relevance
|
|
215
|
+
results.sort(key=lambda x: x.get('relevance', 0.0), reverse=True)
|
|
216
|
+
return results
|
|
217
|
+
|
|
218
|
+
async def add_document(
|
|
219
|
+
self,
|
|
220
|
+
doc_id: str,
|
|
221
|
+
user_id: str,
|
|
222
|
+
title: str,
|
|
223
|
+
filename: str,
|
|
224
|
+
description: str = "",
|
|
225
|
+
tags: List[str] = None,
|
|
226
|
+
**metadata
|
|
227
|
+
) -> bool:
|
|
228
|
+
"""
|
|
229
|
+
Add document metadata.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
doc_id: Document identifier
|
|
233
|
+
user_id: Owner user ID
|
|
234
|
+
title: Document title
|
|
235
|
+
filename: Original filename
|
|
236
|
+
description: Document description
|
|
237
|
+
tags: Document tags
|
|
238
|
+
**metadata: Additional metadata
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
True if successful
|
|
242
|
+
"""
|
|
243
|
+
doc_data = {
|
|
244
|
+
'user_id': user_id,
|
|
245
|
+
'title': title,
|
|
246
|
+
'filename': filename,
|
|
247
|
+
'description': description,
|
|
248
|
+
'tags': tags or [],
|
|
249
|
+
**metadata
|
|
250
|
+
}
|
|
251
|
+
return await self.insert(doc_id, doc_data)
|
|
252
|
+
|
|
253
|
+
async def get_document(self, doc_id: str) -> Optional[Dict[str, Any]]:
|
|
254
|
+
"""Get a specific document's metadata."""
|
|
255
|
+
return await self.get(doc_id)
|
|
256
|
+
|
|
257
|
+
async def delete_document(self, doc_id: str) -> bool:
|
|
258
|
+
""" Delete a document's metadata."""
|
|
259
|
+
return await self.delete(doc_id)
|
|
260
|
+
|
|
261
|
+
async def list_user_documents(
|
|
262
|
+
self,
|
|
263
|
+
user_id: str,
|
|
264
|
+
limit: int = 100
|
|
265
|
+
) -> List[Dict[str, Any]]:
|
|
266
|
+
"""List all documents for a specific user."""
|
|
267
|
+
all_docs = await self.list_all(limit=limit)
|
|
268
|
+
|
|
269
|
+
user_docs = []
|
|
270
|
+
for doc in all_docs:
|
|
271
|
+
doc_data = doc.get('data', {})
|
|
272
|
+
if doc_data.get('user_id') == user_id:
|
|
273
|
+
user_docs.append({
|
|
274
|
+
'document_id': doc['identifier'],
|
|
275
|
+
**doc_data
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
return user_docs
|
|
279
|
+
|
|
280
|
+
async def search_by_tags(
|
|
281
|
+
self,
|
|
282
|
+
tags: List[str],
|
|
283
|
+
user_id: Optional[str] = None,
|
|
284
|
+
match_all: bool = False
|
|
285
|
+
) -> List[Dict[str, Any]]:
|
|
286
|
+
"""
|
|
287
|
+
Search documents by tags.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
tags: List of tags to search for
|
|
291
|
+
user_id: Optional user filter
|
|
292
|
+
match_all: If True, document must have all tags; if False, any tag matches
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
List of matching documents
|
|
296
|
+
"""
|
|
297
|
+
all_docs = await self.list_all()
|
|
298
|
+
results = []
|
|
299
|
+
|
|
300
|
+
for doc in all_docs:
|
|
301
|
+
doc_data = doc.get('data', {})
|
|
302
|
+
|
|
303
|
+
# Filter by user if specified
|
|
304
|
+
if user_id and doc_data.get('user_id') != user_id:
|
|
305
|
+
continue
|
|
306
|
+
|
|
307
|
+
doc_tags = doc_data.get('tags', [])
|
|
308
|
+
|
|
309
|
+
# Check tag matching
|
|
310
|
+
if match_all:
|
|
311
|
+
# Document must have all specified tags
|
|
312
|
+
if all(tag in doc_tags for tag in tags):
|
|
313
|
+
results.append({
|
|
314
|
+
'document_id': doc['identifier'],
|
|
315
|
+
**doc_data
|
|
316
|
+
})
|
|
317
|
+
else:
|
|
318
|
+
# Document must have at least one tag
|
|
319
|
+
if any(tag in doc_tags for tag in tags):
|
|
320
|
+
results.append({
|
|
321
|
+
'document_id': doc['identifier'],
|
|
322
|
+
**doc_data
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
return results
|