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,659 @@
|
|
|
1
|
+
"""Redis-backed knowledge base primitives."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any, Dict, List, Optional, Tuple, Callable, Union
|
|
6
|
+
from duckdb import identifier
|
|
7
|
+
from navconfig.logging import logging
|
|
8
|
+
from datamodel.parsers.json import json_encoder, json_decoder # pylint: disable=E0611 # noqa
|
|
9
|
+
from redis.asyncio import Redis
|
|
10
|
+
from .abstract import AbstractKnowledgeBase
|
|
11
|
+
from ...conf import REDIS_HISTORY_URL
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RedisKnowledgeBase(AbstractKnowledgeBase):
|
|
15
|
+
"""
|
|
16
|
+
Generic Redis-based Knowledge Base with CRUD operations.
|
|
17
|
+
|
|
18
|
+
Supports both hash storage (HSET/HGET) and simple key-value storage (SET/GET).
|
|
19
|
+
Provides flexible search, filtering, and data management capabilities.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
*,
|
|
25
|
+
name: str,
|
|
26
|
+
category: str,
|
|
27
|
+
namespace: str,
|
|
28
|
+
redis_url: str | None = None,
|
|
29
|
+
decode_responses: bool = True,
|
|
30
|
+
encoding: str = "utf-8",
|
|
31
|
+
ttl: Optional[int] = None,
|
|
32
|
+
use_hash_storage: bool = True,
|
|
33
|
+
activation_patterns: Optional[List[str]] = None,
|
|
34
|
+
**kwargs: Any,
|
|
35
|
+
) -> None:
|
|
36
|
+
"""Configure the Redis connection and base KB metadata.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
name: Name of the knowledge base
|
|
40
|
+
category: Category identifier
|
|
41
|
+
namespace: Prefix for Redis keys (e.g., 'user_prefs', 'bot_settings')
|
|
42
|
+
activation_patterns: Patterns that activate this KB
|
|
43
|
+
redis_url: Redis connection URL
|
|
44
|
+
use_hash_storage: Use Redis hashes vs simple key-value
|
|
45
|
+
ttl: Default TTL in seconds for keys (None = no expiration)
|
|
46
|
+
**kwargs: Additional arguments passed to parent
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
super().__init__(
|
|
50
|
+
name=name,
|
|
51
|
+
category=category,
|
|
52
|
+
activation_patterns=activation_patterns or [],
|
|
53
|
+
**kwargs
|
|
54
|
+
)
|
|
55
|
+
self.namespace = namespace
|
|
56
|
+
self.redis_url = redis_url or REDIS_HISTORY_URL
|
|
57
|
+
self.use_hash_storage = use_hash_storage
|
|
58
|
+
self.default_ttl = ttl
|
|
59
|
+
self.redis = Redis.from_url(
|
|
60
|
+
self.redis_url,
|
|
61
|
+
decode_responses=decode_responses,
|
|
62
|
+
encoding=encoding,
|
|
63
|
+
socket_connect_timeout=5,
|
|
64
|
+
socket_timeout=5,
|
|
65
|
+
retry_on_timeout=True,
|
|
66
|
+
)
|
|
67
|
+
self.logger = logging.getLogger(__name__)
|
|
68
|
+
|
|
69
|
+
async def should_activate(self, query: str, context: Dict[str, Any]) -> Tuple[bool, float]:
|
|
70
|
+
"""Default activation strategy based on configured patterns."""
|
|
71
|
+
|
|
72
|
+
if self.always_active:
|
|
73
|
+
return True, 1.0
|
|
74
|
+
|
|
75
|
+
query_lower = (query or "").lower()
|
|
76
|
+
return next(
|
|
77
|
+
(
|
|
78
|
+
(True, 0.8)
|
|
79
|
+
for pattern in self.activation_patterns
|
|
80
|
+
if pattern in query_lower
|
|
81
|
+
),
|
|
82
|
+
(False, 0.0),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def _data_matches(
|
|
86
|
+
self,
|
|
87
|
+
data,
|
|
88
|
+
query: str,
|
|
89
|
+
identifier: str,
|
|
90
|
+
field_filter: Optional[List[str]] = None,
|
|
91
|
+
match_fn: Optional[Callable] = None
|
|
92
|
+
) -> Optional[Dict[str, Any]]:
|
|
93
|
+
"""Check if data matches the query."""
|
|
94
|
+
if data and self._matches_query(data, query, field_filter, match_fn):
|
|
95
|
+
return {
|
|
96
|
+
'identifier': identifier,
|
|
97
|
+
'data': data,
|
|
98
|
+
'relevance': self._calculate_relevance(data, query)
|
|
99
|
+
}
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
async def search(
|
|
103
|
+
self,
|
|
104
|
+
query: str,
|
|
105
|
+
*,
|
|
106
|
+
identifier: Optional[str] = None,
|
|
107
|
+
field_filter: Optional[List[str]] = None,
|
|
108
|
+
match_fn: Optional[Callable] = None,
|
|
109
|
+
limit: int = 100,
|
|
110
|
+
**kwargs: Any,
|
|
111
|
+
) -> List[Dict[str, Any]]:
|
|
112
|
+
"""
|
|
113
|
+
Search for entries matching the query.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
query: Search query string
|
|
117
|
+
identifier: Specific identifier to search in (optional)
|
|
118
|
+
field_filter: Only search in these fields
|
|
119
|
+
match_fn: Custom matching function(data, query) -> bool
|
|
120
|
+
limit: Maximum results to return
|
|
121
|
+
**kwargs: Additional filters
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
List of matching entries with metadata
|
|
125
|
+
"""
|
|
126
|
+
query_lower = (query or "").lower()
|
|
127
|
+
results = []
|
|
128
|
+
|
|
129
|
+
if identifier:
|
|
130
|
+
# Search in specific identifier
|
|
131
|
+
data = await self.get(identifier, **kwargs)
|
|
132
|
+
if rst := self._data_matches(data, query_lower, identifier, field_filter, match_fn):
|
|
133
|
+
results.append(rst)
|
|
134
|
+
else:
|
|
135
|
+
# Search across all keys with pattern
|
|
136
|
+
pattern = self._get_key('*', *list(kwargs.values()))
|
|
137
|
+
cursor = 0
|
|
138
|
+
|
|
139
|
+
while True:
|
|
140
|
+
cursor, keys = await self.redis.scan(
|
|
141
|
+
cursor,
|
|
142
|
+
match=pattern,
|
|
143
|
+
count=100
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
for key in keys:
|
|
147
|
+
if len(results) >= limit:
|
|
148
|
+
break
|
|
149
|
+
|
|
150
|
+
# Extract identifier from key
|
|
151
|
+
key_parts = key.split(':')
|
|
152
|
+
if len(key_parts) >= 2:
|
|
153
|
+
key_identifier = key_parts[1]
|
|
154
|
+
|
|
155
|
+
data = await self.get(key_identifier, **kwargs)
|
|
156
|
+
if rst := self._data_matches(data, query_lower, key_identifier, field_filter, match_fn):
|
|
157
|
+
results.append(rst)
|
|
158
|
+
|
|
159
|
+
if cursor == 0 or len(results) >= limit:
|
|
160
|
+
break
|
|
161
|
+
|
|
162
|
+
# Sort by relevance
|
|
163
|
+
results.sort(key=lambda x: x['relevance'], reverse=True)
|
|
164
|
+
return results[:limit]
|
|
165
|
+
|
|
166
|
+
def _matches_query(
|
|
167
|
+
self,
|
|
168
|
+
data: Any,
|
|
169
|
+
query: str,
|
|
170
|
+
field_filter: Optional[List[str]] = None,
|
|
171
|
+
match_fn: Optional[Callable] = None
|
|
172
|
+
) -> bool:
|
|
173
|
+
"""Check if data matches the query."""
|
|
174
|
+
if match_fn:
|
|
175
|
+
return match_fn(data, query)
|
|
176
|
+
|
|
177
|
+
if isinstance(data, dict):
|
|
178
|
+
fields = field_filter or data.keys()
|
|
179
|
+
for field in fields:
|
|
180
|
+
value = data.get(field)
|
|
181
|
+
if value and query in str(value).lower():
|
|
182
|
+
return True
|
|
183
|
+
elif isinstance(data, str):
|
|
184
|
+
return query in data.lower()
|
|
185
|
+
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
def _calculate_relevance(self, data: Any, query: str) -> float:
|
|
189
|
+
"""Calculate relevance score for search results."""
|
|
190
|
+
if isinstance(data, dict):
|
|
191
|
+
score = 0.0
|
|
192
|
+
for value in data.values():
|
|
193
|
+
if value and query in str(value).lower():
|
|
194
|
+
# Exact match gets higher score
|
|
195
|
+
score += 1.0 if query == str(value).lower() else 0.5
|
|
196
|
+
return score
|
|
197
|
+
elif isinstance(data, str):
|
|
198
|
+
if query == data.lower():
|
|
199
|
+
return 1.0
|
|
200
|
+
elif query in data.lower():
|
|
201
|
+
return 0.5
|
|
202
|
+
return 0.0
|
|
203
|
+
|
|
204
|
+
async def list_all(
|
|
205
|
+
self,
|
|
206
|
+
pattern: Optional[str] = None,
|
|
207
|
+
limit: int = 1000
|
|
208
|
+
) -> List[Dict[str, Any]]:
|
|
209
|
+
"""
|
|
210
|
+
List all entries matching a pattern.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
pattern: Key pattern (uses wildcard if None)
|
|
214
|
+
limit: Maximum results
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
List of all matching entries
|
|
218
|
+
"""
|
|
219
|
+
results = []
|
|
220
|
+
search_pattern = pattern or f"{self.namespace}:*"
|
|
221
|
+
cursor = 0
|
|
222
|
+
|
|
223
|
+
while True:
|
|
224
|
+
cursor, keys = await self.redis.scan(
|
|
225
|
+
cursor,
|
|
226
|
+
match=search_pattern,
|
|
227
|
+
count=100
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
for key in keys:
|
|
231
|
+
if len(results) >= limit:
|
|
232
|
+
break
|
|
233
|
+
|
|
234
|
+
# Extract identifier
|
|
235
|
+
key_parts = key.split(':')
|
|
236
|
+
if len(key_parts) >= 2:
|
|
237
|
+
identifier = key_parts[1]
|
|
238
|
+
data = await self.get(identifier)
|
|
239
|
+
if data:
|
|
240
|
+
results.append({
|
|
241
|
+
'identifier': identifier,
|
|
242
|
+
'key': key,
|
|
243
|
+
'data': data
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
if cursor == 0 or len(results) >= limit:
|
|
247
|
+
break
|
|
248
|
+
|
|
249
|
+
return results
|
|
250
|
+
|
|
251
|
+
async def count(self, pattern: Optional[str] = None) -> int:
|
|
252
|
+
"""
|
|
253
|
+
Count entries matching a pattern.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
pattern: Key pattern (uses wildcard if None)
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Number of matching keys
|
|
260
|
+
"""
|
|
261
|
+
search_pattern = pattern or f"{self.namespace}:*"
|
|
262
|
+
count = 0
|
|
263
|
+
cursor = 0
|
|
264
|
+
|
|
265
|
+
while True:
|
|
266
|
+
cursor, keys = await self.redis.scan(
|
|
267
|
+
cursor,
|
|
268
|
+
match=search_pattern,
|
|
269
|
+
count=1000
|
|
270
|
+
)
|
|
271
|
+
count += len(keys)
|
|
272
|
+
|
|
273
|
+
if cursor == 0:
|
|
274
|
+
break
|
|
275
|
+
|
|
276
|
+
return count
|
|
277
|
+
|
|
278
|
+
def _get_key(self, identifier: str, *args) -> str:
|
|
279
|
+
"""
|
|
280
|
+
Generate Redis key with namespace.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
identifier: Primary identifier (e.g., user_id, chatbot_id)
|
|
284
|
+
*args: Additional key components
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Formatted Redis key
|
|
288
|
+
"""
|
|
289
|
+
parts = [self.namespace, identifier]
|
|
290
|
+
parts.extend(str(arg) for arg in args)
|
|
291
|
+
return ":".join(parts)
|
|
292
|
+
|
|
293
|
+
def _serialize_data(self, data: Any) -> str:
|
|
294
|
+
"""Serialize data to JSON string."""
|
|
295
|
+
try:
|
|
296
|
+
return json_encoder(data)
|
|
297
|
+
except Exception:
|
|
298
|
+
try:
|
|
299
|
+
return json.dumps(data, ensure_ascii=False, separators=(',', ':'), default=str)
|
|
300
|
+
except Exception:
|
|
301
|
+
return str(data)
|
|
302
|
+
|
|
303
|
+
def _deserialize_data(self, data: str) -> Any:
|
|
304
|
+
"""Deserialize JSON string to Python object."""
|
|
305
|
+
try:
|
|
306
|
+
return json_decoder(data)
|
|
307
|
+
except Exception:
|
|
308
|
+
try:
|
|
309
|
+
return json.loads(data)
|
|
310
|
+
except Exception:
|
|
311
|
+
return data
|
|
312
|
+
|
|
313
|
+
# ========== Utility Methods ==========
|
|
314
|
+
|
|
315
|
+
async def clear_all(self, pattern: Optional[str] = None) -> int:
|
|
316
|
+
"""
|
|
317
|
+
Delete all entries matching a pattern.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
pattern: Key pattern (uses prefix if None)
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
Number of keys deleted
|
|
324
|
+
"""
|
|
325
|
+
search_pattern = pattern or f"{self.namespace}:*"
|
|
326
|
+
deleted = 0
|
|
327
|
+
cursor = 0
|
|
328
|
+
|
|
329
|
+
while True:
|
|
330
|
+
cursor, keys = await self.redis.scan(
|
|
331
|
+
cursor,
|
|
332
|
+
match=search_pattern,
|
|
333
|
+
count=100
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
if keys:
|
|
337
|
+
deleted += await self.redis.delete(*keys)
|
|
338
|
+
|
|
339
|
+
if cursor == 0:
|
|
340
|
+
break
|
|
341
|
+
|
|
342
|
+
return deleted
|
|
343
|
+
|
|
344
|
+
async def set_ttl(self, identifier: str, ttl: int, **kwargs) -> bool:
|
|
345
|
+
"""
|
|
346
|
+
Set TTL for a key.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
identifier: Primary identifier
|
|
350
|
+
ttl: TTL in seconds
|
|
351
|
+
**kwargs: Additional key components
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
True if successful
|
|
355
|
+
"""
|
|
356
|
+
key = self._get_key(identifier, *kwargs.values())
|
|
357
|
+
try:
|
|
358
|
+
return await self.redis.expire(key, ttl) > 0
|
|
359
|
+
except Exception:
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
async def get_ttl(self, identifier: str, **kwargs) -> Optional[int]:
|
|
363
|
+
"""
|
|
364
|
+
Get remaining TTL for a key.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
identifier: Primary identifier
|
|
368
|
+
**kwargs: Additional key components
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
TTL in seconds or None
|
|
372
|
+
"""
|
|
373
|
+
key = self._get_key(identifier, *kwargs.values())
|
|
374
|
+
try:
|
|
375
|
+
ttl = await self.redis.ttl(key)
|
|
376
|
+
return ttl if ttl > 0 else None
|
|
377
|
+
except Exception as e:
|
|
378
|
+
self.logger.error(
|
|
379
|
+
f"Error getting TTL for {key}: {e}"
|
|
380
|
+
)
|
|
381
|
+
return None
|
|
382
|
+
|
|
383
|
+
async def ping(self) -> bool:
|
|
384
|
+
"""Test Redis connection."""
|
|
385
|
+
try:
|
|
386
|
+
await self.redis.ping()
|
|
387
|
+
return True
|
|
388
|
+
except Exception as e:
|
|
389
|
+
self.logger.error(
|
|
390
|
+
f"Redis ping failed: {e}"
|
|
391
|
+
)
|
|
392
|
+
return False
|
|
393
|
+
|
|
394
|
+
async def close(self):
|
|
395
|
+
"""Close Redis connection."""
|
|
396
|
+
try:
|
|
397
|
+
await self.redis.close()
|
|
398
|
+
except Exception as e:
|
|
399
|
+
self.logger.error(
|
|
400
|
+
f"Error closing Redis connection: {e}"
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# ========== CRUD Operations ==========
|
|
404
|
+
|
|
405
|
+
async def insert(
|
|
406
|
+
self,
|
|
407
|
+
identifier: str,
|
|
408
|
+
data: Union[Dict[str, Any], str, Any],
|
|
409
|
+
field: Optional[str] = None,
|
|
410
|
+
ttl: Optional[int] = None,
|
|
411
|
+
**kwargs
|
|
412
|
+
) -> bool:
|
|
413
|
+
"""
|
|
414
|
+
Insert or update data in Redis.
|
|
415
|
+
|
|
416
|
+
Args:
|
|
417
|
+
identifier: Primary identifier for the key
|
|
418
|
+
data: Data to store (dict for hash, any for simple storage)
|
|
419
|
+
field: Field name (for hash storage only)
|
|
420
|
+
ttl: TTL in seconds (overrides default)
|
|
421
|
+
**kwargs: Additional key components
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
True if successful
|
|
425
|
+
"""
|
|
426
|
+
key = self._get_key(identifier, *kwargs.values())
|
|
427
|
+
|
|
428
|
+
try:
|
|
429
|
+
if self.use_hash_storage:
|
|
430
|
+
if isinstance(data, dict):
|
|
431
|
+
# Store entire dict as hash
|
|
432
|
+
serialized = {
|
|
433
|
+
k: v if isinstance(v, str) else self._serialize_data(v)
|
|
434
|
+
for k, v in data.items()
|
|
435
|
+
}
|
|
436
|
+
await self.redis.hset(key, mapping=serialized)
|
|
437
|
+
elif field:
|
|
438
|
+
# Store single field in hash
|
|
439
|
+
value = data if isinstance(data, str) else self._serialize_data(data)
|
|
440
|
+
await self.redis.hset(key, field, value)
|
|
441
|
+
else:
|
|
442
|
+
raise ValueError("For hash storage, provide dict or field name")
|
|
443
|
+
else:
|
|
444
|
+
# Simple key-value storage
|
|
445
|
+
value = data if isinstance(data, str) else self._serialize_data(data)
|
|
446
|
+
await self.redis.set(key, value)
|
|
447
|
+
|
|
448
|
+
# Set TTL if specified
|
|
449
|
+
if expiry := ttl or self.default_ttl:
|
|
450
|
+
await self.redis.expire(key, expiry)
|
|
451
|
+
|
|
452
|
+
return True
|
|
453
|
+
|
|
454
|
+
except Exception as e:
|
|
455
|
+
self.logger.error(f"Error inserting data to {key}: {e}")
|
|
456
|
+
return False
|
|
457
|
+
|
|
458
|
+
async def get(
|
|
459
|
+
self,
|
|
460
|
+
identifier: str,
|
|
461
|
+
field: Optional[str] = None,
|
|
462
|
+
default: Any = None,
|
|
463
|
+
**kwargs
|
|
464
|
+
) -> Any:
|
|
465
|
+
"""
|
|
466
|
+
Retrieve data from Redis.
|
|
467
|
+
|
|
468
|
+
Args:
|
|
469
|
+
identifier: Primary identifier for the key
|
|
470
|
+
field: Field name (for hash storage only)
|
|
471
|
+
default: Default value if not found
|
|
472
|
+
**kwargs: Additional key components
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
Retrieved data or default
|
|
476
|
+
"""
|
|
477
|
+
key = self._get_key(identifier, *kwargs.values())
|
|
478
|
+
|
|
479
|
+
try:
|
|
480
|
+
if self.use_hash_storage:
|
|
481
|
+
if field:
|
|
482
|
+
# Get single field
|
|
483
|
+
value = await self.redis.hget(key, field)
|
|
484
|
+
if value is None:
|
|
485
|
+
return default
|
|
486
|
+
return self._deserialize_data(value) if value else default
|
|
487
|
+
else:
|
|
488
|
+
# Get all fields
|
|
489
|
+
data = await self.redis.hgetall(key)
|
|
490
|
+
if not data:
|
|
491
|
+
return default
|
|
492
|
+
# Deserialize values
|
|
493
|
+
return {
|
|
494
|
+
k: self._deserialize_data(v) if v else v
|
|
495
|
+
for k, v in data.items()
|
|
496
|
+
}
|
|
497
|
+
else:
|
|
498
|
+
# Simple key-value storage
|
|
499
|
+
value = await self.redis.get(key)
|
|
500
|
+
if value is None:
|
|
501
|
+
return default
|
|
502
|
+
return self._deserialize_data(value) if value else default
|
|
503
|
+
|
|
504
|
+
except Exception as e:
|
|
505
|
+
self.logger.error(f"Error getting data from {key}: {e}")
|
|
506
|
+
return default
|
|
507
|
+
|
|
508
|
+
async def update(
|
|
509
|
+
self,
|
|
510
|
+
identifier: str,
|
|
511
|
+
data: Union[Dict[str, Any], Any],
|
|
512
|
+
field: Optional[str] = None,
|
|
513
|
+
**kwargs
|
|
514
|
+
) -> bool:
|
|
515
|
+
"""
|
|
516
|
+
Update existing data (alias for insert with merge capability).
|
|
517
|
+
|
|
518
|
+
Args:
|
|
519
|
+
identifier: Primary identifier
|
|
520
|
+
data: Data to update
|
|
521
|
+
field: Field name (for hash storage)
|
|
522
|
+
**kwargs: Additional key components
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
True if successful
|
|
526
|
+
"""
|
|
527
|
+
return await self.insert(identifier, data, field=field, **kwargs)
|
|
528
|
+
|
|
529
|
+
async def delete(
|
|
530
|
+
self,
|
|
531
|
+
identifier: str,
|
|
532
|
+
field: Optional[str] = None,
|
|
533
|
+
**kwargs
|
|
534
|
+
) -> bool:
|
|
535
|
+
"""
|
|
536
|
+
Delete data from Redis.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
identifier: Primary identifier
|
|
540
|
+
field: Field name to delete (for hash storage only)
|
|
541
|
+
**kwargs: Additional key components
|
|
542
|
+
|
|
543
|
+
Returns:
|
|
544
|
+
True if successful
|
|
545
|
+
"""
|
|
546
|
+
key = self._get_key(identifier, *kwargs.values())
|
|
547
|
+
|
|
548
|
+
try:
|
|
549
|
+
if self.use_hash_storage and field:
|
|
550
|
+
# Delete specific field from hash
|
|
551
|
+
result = await self.redis.hdel(key, field)
|
|
552
|
+
else:
|
|
553
|
+
# Delete entire key
|
|
554
|
+
result = await self.redis.delete(key)
|
|
555
|
+
return result > 0
|
|
556
|
+
|
|
557
|
+
except Exception as e:
|
|
558
|
+
self.logger.error(f"Error deleting {key}: {e}")
|
|
559
|
+
return False
|
|
560
|
+
|
|
561
|
+
async def exists(self, identifier: str, field: Optional[str] = None, **kwargs) -> bool:
|
|
562
|
+
"""
|
|
563
|
+
Check if key or field exists.
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
identifier: Primary identifier
|
|
567
|
+
field: Field name (for hash storage)
|
|
568
|
+
**kwargs: Additional key components
|
|
569
|
+
|
|
570
|
+
Returns:
|
|
571
|
+
True if exists
|
|
572
|
+
"""
|
|
573
|
+
key = self._get_key(identifier, *kwargs.values())
|
|
574
|
+
|
|
575
|
+
try:
|
|
576
|
+
if self.use_hash_storage and field:
|
|
577
|
+
return await self.redis.hexists(key, field)
|
|
578
|
+
else:
|
|
579
|
+
return await self.redis.exists(key) > 0
|
|
580
|
+
except Exception as e:
|
|
581
|
+
self.logger.error(
|
|
582
|
+
f"Error checking existence of {key}: {e}"
|
|
583
|
+
)
|
|
584
|
+
return False
|
|
585
|
+
|
|
586
|
+
# ========== Bulk Operations ==========
|
|
587
|
+
|
|
588
|
+
async def bulk_insert(
|
|
589
|
+
self,
|
|
590
|
+
items: List[Dict[str, Any]],
|
|
591
|
+
identifier_key: str = 'id',
|
|
592
|
+
ttl: Optional[int] = None
|
|
593
|
+
) -> int:
|
|
594
|
+
"""
|
|
595
|
+
Insert multiple items in bulk.
|
|
596
|
+
|
|
597
|
+
Args:
|
|
598
|
+
items: List of items to insert
|
|
599
|
+
identifier_key: Key name containing the identifier
|
|
600
|
+
ttl: TTL for all items
|
|
601
|
+
|
|
602
|
+
Returns:
|
|
603
|
+
Number of items successfully inserted
|
|
604
|
+
"""
|
|
605
|
+
count = 0
|
|
606
|
+
for item in items:
|
|
607
|
+
identifier = item.get(identifier_key)
|
|
608
|
+
if not identifier:
|
|
609
|
+
continue
|
|
610
|
+
|
|
611
|
+
if await self.insert(identifier, item, ttl=ttl):
|
|
612
|
+
count += 1
|
|
613
|
+
|
|
614
|
+
return count
|
|
615
|
+
|
|
616
|
+
async def bulk_get(
|
|
617
|
+
self,
|
|
618
|
+
identifiers: List[str],
|
|
619
|
+
**kwargs
|
|
620
|
+
) -> Dict[str, Any]:
|
|
621
|
+
"""
|
|
622
|
+
Retrieve multiple items in bulk.
|
|
623
|
+
|
|
624
|
+
Args:
|
|
625
|
+
identifiers: List of identifiers
|
|
626
|
+
**kwargs: Additional key components
|
|
627
|
+
|
|
628
|
+
Returns:
|
|
629
|
+
Dict mapping identifier to data
|
|
630
|
+
"""
|
|
631
|
+
results = {}
|
|
632
|
+
for identifier in identifiers:
|
|
633
|
+
data = await self.get(identifier, **kwargs)
|
|
634
|
+
if data is not None:
|
|
635
|
+
results[identifier] = data
|
|
636
|
+
|
|
637
|
+
return results
|
|
638
|
+
|
|
639
|
+
async def bulk_delete(
|
|
640
|
+
self,
|
|
641
|
+
identifiers: List[str],
|
|
642
|
+
**kwargs
|
|
643
|
+
) -> int:
|
|
644
|
+
"""
|
|
645
|
+
Delete multiple items in bulk.
|
|
646
|
+
|
|
647
|
+
Args:
|
|
648
|
+
identifiers: List of identifiers to delete
|
|
649
|
+
**kwargs: Additional key components
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
Number of items deleted
|
|
653
|
+
"""
|
|
654
|
+
count = 0
|
|
655
|
+
for identifier in identifiers:
|
|
656
|
+
if await self.delete(identifier, **kwargs):
|
|
657
|
+
count += 1
|
|
658
|
+
|
|
659
|
+
return count
|