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.
Files changed (535) hide show
  1. agentui/.prettierrc +15 -0
  2. agentui/QUICKSTART.md +272 -0
  3. agentui/README.md +59 -0
  4. agentui/env.example +16 -0
  5. agentui/jsconfig.json +14 -0
  6. agentui/package-lock.json +4242 -0
  7. agentui/package.json +34 -0
  8. agentui/scripts/postinstall/apply-patches.mjs +260 -0
  9. agentui/src/app.css +61 -0
  10. agentui/src/app.d.ts +13 -0
  11. agentui/src/app.html +12 -0
  12. agentui/src/components/LoadingSpinner.svelte +64 -0
  13. agentui/src/components/ThemeSwitcher.svelte +159 -0
  14. agentui/src/components/index.js +4 -0
  15. agentui/src/lib/api/bots.ts +60 -0
  16. agentui/src/lib/api/chat.ts +22 -0
  17. agentui/src/lib/api/http.ts +25 -0
  18. agentui/src/lib/components/BotCard.svelte +33 -0
  19. agentui/src/lib/components/ChatBubble.svelte +63 -0
  20. agentui/src/lib/components/Toast.svelte +21 -0
  21. agentui/src/lib/config.ts +20 -0
  22. agentui/src/lib/stores/auth.svelte.ts +73 -0
  23. agentui/src/lib/stores/theme.svelte.js +64 -0
  24. agentui/src/lib/stores/toast.svelte.ts +31 -0
  25. agentui/src/lib/utils/conversation.ts +39 -0
  26. agentui/src/routes/+layout.svelte +20 -0
  27. agentui/src/routes/+page.svelte +232 -0
  28. agentui/src/routes/login/+page.svelte +200 -0
  29. agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
  30. agentui/src/routes/talk/[agentId]/+page.ts +7 -0
  31. agentui/static/README.md +1 -0
  32. agentui/svelte.config.js +11 -0
  33. agentui/tailwind.config.ts +53 -0
  34. agentui/tsconfig.json +3 -0
  35. agentui/vite.config.ts +10 -0
  36. ai_parrot-0.17.2.dist-info/METADATA +472 -0
  37. ai_parrot-0.17.2.dist-info/RECORD +535 -0
  38. ai_parrot-0.17.2.dist-info/WHEEL +6 -0
  39. ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
  40. ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
  41. ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
  42. crew-builder/.prettierrc +15 -0
  43. crew-builder/QUICKSTART.md +259 -0
  44. crew-builder/README.md +113 -0
  45. crew-builder/env.example +17 -0
  46. crew-builder/jsconfig.json +14 -0
  47. crew-builder/package-lock.json +4182 -0
  48. crew-builder/package.json +37 -0
  49. crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
  50. crew-builder/src/app.css +62 -0
  51. crew-builder/src/app.d.ts +13 -0
  52. crew-builder/src/app.html +12 -0
  53. crew-builder/src/components/LoadingSpinner.svelte +64 -0
  54. crew-builder/src/components/ThemeSwitcher.svelte +149 -0
  55. crew-builder/src/components/index.js +9 -0
  56. crew-builder/src/lib/api/bots.ts +60 -0
  57. crew-builder/src/lib/api/chat.ts +80 -0
  58. crew-builder/src/lib/api/client.ts +56 -0
  59. crew-builder/src/lib/api/crew/crew.ts +136 -0
  60. crew-builder/src/lib/api/index.ts +5 -0
  61. crew-builder/src/lib/api/o365/auth.ts +65 -0
  62. crew-builder/src/lib/auth/auth.ts +54 -0
  63. crew-builder/src/lib/components/AgentNode.svelte +43 -0
  64. crew-builder/src/lib/components/BotCard.svelte +33 -0
  65. crew-builder/src/lib/components/ChatBubble.svelte +67 -0
  66. crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
  67. crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
  68. crew-builder/src/lib/components/JsonViewer.svelte +24 -0
  69. crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
  70. crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
  71. crew-builder/src/lib/components/Toast.svelte +67 -0
  72. crew-builder/src/lib/components/Toolbar.svelte +157 -0
  73. crew-builder/src/lib/components/index.ts +10 -0
  74. crew-builder/src/lib/config.ts +8 -0
  75. crew-builder/src/lib/stores/auth.svelte.ts +228 -0
  76. crew-builder/src/lib/stores/crewStore.ts +369 -0
  77. crew-builder/src/lib/stores/theme.svelte.js +145 -0
  78. crew-builder/src/lib/stores/toast.svelte.ts +69 -0
  79. crew-builder/src/lib/utils/conversation.ts +39 -0
  80. crew-builder/src/lib/utils/markdown.ts +122 -0
  81. crew-builder/src/lib/utils/talkHistory.ts +47 -0
  82. crew-builder/src/routes/+layout.svelte +20 -0
  83. crew-builder/src/routes/+page.svelte +539 -0
  84. crew-builder/src/routes/agents/+page.svelte +247 -0
  85. crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
  86. crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
  87. crew-builder/src/routes/builder/+page.svelte +204 -0
  88. crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
  89. crew-builder/src/routes/crew/ask/+page.ts +1 -0
  90. crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
  91. crew-builder/src/routes/login/+page.svelte +197 -0
  92. crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
  93. crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
  94. crew-builder/static/README.md +1 -0
  95. crew-builder/svelte.config.js +11 -0
  96. crew-builder/tailwind.config.ts +53 -0
  97. crew-builder/tsconfig.json +3 -0
  98. crew-builder/vite.config.ts +10 -0
  99. mcp_servers/calculator_server.py +309 -0
  100. parrot/__init__.py +27 -0
  101. parrot/__pycache__/__init__.cpython-310.pyc +0 -0
  102. parrot/__pycache__/version.cpython-310.pyc +0 -0
  103. parrot/_version.py +34 -0
  104. parrot/a2a/__init__.py +48 -0
  105. parrot/a2a/client.py +658 -0
  106. parrot/a2a/discovery.py +89 -0
  107. parrot/a2a/mixin.py +257 -0
  108. parrot/a2a/models.py +376 -0
  109. parrot/a2a/server.py +770 -0
  110. parrot/agents/__init__.py +29 -0
  111. parrot/bots/__init__.py +12 -0
  112. parrot/bots/a2a_agent.py +19 -0
  113. parrot/bots/abstract.py +3139 -0
  114. parrot/bots/agent.py +1129 -0
  115. parrot/bots/basic.py +9 -0
  116. parrot/bots/chatbot.py +669 -0
  117. parrot/bots/data.py +1618 -0
  118. parrot/bots/database/__init__.py +5 -0
  119. parrot/bots/database/abstract.py +3071 -0
  120. parrot/bots/database/cache.py +286 -0
  121. parrot/bots/database/models.py +468 -0
  122. parrot/bots/database/prompts.py +154 -0
  123. parrot/bots/database/retries.py +98 -0
  124. parrot/bots/database/router.py +269 -0
  125. parrot/bots/database/sql.py +41 -0
  126. parrot/bots/db/__init__.py +6 -0
  127. parrot/bots/db/abstract.py +556 -0
  128. parrot/bots/db/bigquery.py +602 -0
  129. parrot/bots/db/cache.py +85 -0
  130. parrot/bots/db/documentdb.py +668 -0
  131. parrot/bots/db/elastic.py +1014 -0
  132. parrot/bots/db/influx.py +898 -0
  133. parrot/bots/db/mock.py +96 -0
  134. parrot/bots/db/multi.py +783 -0
  135. parrot/bots/db/prompts.py +185 -0
  136. parrot/bots/db/sql.py +1255 -0
  137. parrot/bots/db/tools.py +212 -0
  138. parrot/bots/document.py +680 -0
  139. parrot/bots/hrbot.py +15 -0
  140. parrot/bots/kb.py +170 -0
  141. parrot/bots/mcp.py +36 -0
  142. parrot/bots/orchestration/README.md +463 -0
  143. parrot/bots/orchestration/__init__.py +1 -0
  144. parrot/bots/orchestration/agent.py +155 -0
  145. parrot/bots/orchestration/crew.py +3330 -0
  146. parrot/bots/orchestration/fsm.py +1179 -0
  147. parrot/bots/orchestration/hr.py +434 -0
  148. parrot/bots/orchestration/storage/__init__.py +4 -0
  149. parrot/bots/orchestration/storage/memory.py +100 -0
  150. parrot/bots/orchestration/storage/mixin.py +119 -0
  151. parrot/bots/orchestration/verify.py +202 -0
  152. parrot/bots/product.py +204 -0
  153. parrot/bots/prompts/__init__.py +96 -0
  154. parrot/bots/prompts/agents.py +155 -0
  155. parrot/bots/prompts/data.py +216 -0
  156. parrot/bots/prompts/output_generation.py +8 -0
  157. parrot/bots/scraper/__init__.py +3 -0
  158. parrot/bots/scraper/models.py +122 -0
  159. parrot/bots/scraper/scraper.py +1173 -0
  160. parrot/bots/scraper/templates.py +115 -0
  161. parrot/bots/stores/__init__.py +5 -0
  162. parrot/bots/stores/local.py +172 -0
  163. parrot/bots/webdev.py +81 -0
  164. parrot/cli.py +17 -0
  165. parrot/clients/__init__.py +16 -0
  166. parrot/clients/base.py +1491 -0
  167. parrot/clients/claude.py +1191 -0
  168. parrot/clients/factory.py +129 -0
  169. parrot/clients/google.py +4567 -0
  170. parrot/clients/gpt.py +1975 -0
  171. parrot/clients/grok.py +432 -0
  172. parrot/clients/groq.py +986 -0
  173. parrot/clients/hf.py +582 -0
  174. parrot/clients/models.py +18 -0
  175. parrot/conf.py +395 -0
  176. parrot/embeddings/__init__.py +9 -0
  177. parrot/embeddings/base.py +157 -0
  178. parrot/embeddings/google.py +98 -0
  179. parrot/embeddings/huggingface.py +74 -0
  180. parrot/embeddings/openai.py +84 -0
  181. parrot/embeddings/processor.py +88 -0
  182. parrot/exceptions.c +13868 -0
  183. parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
  184. parrot/exceptions.pxd +22 -0
  185. parrot/exceptions.pxi +15 -0
  186. parrot/exceptions.pyx +44 -0
  187. parrot/generators/__init__.py +29 -0
  188. parrot/generators/base.py +200 -0
  189. parrot/generators/html.py +293 -0
  190. parrot/generators/react.py +205 -0
  191. parrot/generators/streamlit.py +203 -0
  192. parrot/generators/template.py +105 -0
  193. parrot/handlers/__init__.py +4 -0
  194. parrot/handlers/agent.py +861 -0
  195. parrot/handlers/agents/__init__.py +1 -0
  196. parrot/handlers/agents/abstract.py +900 -0
  197. parrot/handlers/bots.py +338 -0
  198. parrot/handlers/chat.py +915 -0
  199. parrot/handlers/creation.sql +192 -0
  200. parrot/handlers/crew/ARCHITECTURE.md +362 -0
  201. parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
  202. parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
  203. parrot/handlers/crew/__init__.py +0 -0
  204. parrot/handlers/crew/handler.py +801 -0
  205. parrot/handlers/crew/models.py +229 -0
  206. parrot/handlers/crew/redis_persistence.py +523 -0
  207. parrot/handlers/jobs/__init__.py +10 -0
  208. parrot/handlers/jobs/job.py +384 -0
  209. parrot/handlers/jobs/mixin.py +627 -0
  210. parrot/handlers/jobs/models.py +115 -0
  211. parrot/handlers/jobs/worker.py +31 -0
  212. parrot/handlers/models.py +596 -0
  213. parrot/handlers/o365_auth.py +105 -0
  214. parrot/handlers/stream.py +337 -0
  215. parrot/interfaces/__init__.py +6 -0
  216. parrot/interfaces/aws.py +143 -0
  217. parrot/interfaces/credentials.py +113 -0
  218. parrot/interfaces/database.py +27 -0
  219. parrot/interfaces/google.py +1123 -0
  220. parrot/interfaces/hierarchy.py +1227 -0
  221. parrot/interfaces/http.py +651 -0
  222. parrot/interfaces/images/__init__.py +0 -0
  223. parrot/interfaces/images/plugins/__init__.py +24 -0
  224. parrot/interfaces/images/plugins/abstract.py +58 -0
  225. parrot/interfaces/images/plugins/analisys.py +148 -0
  226. parrot/interfaces/images/plugins/classify.py +150 -0
  227. parrot/interfaces/images/plugins/classifybase.py +182 -0
  228. parrot/interfaces/images/plugins/detect.py +150 -0
  229. parrot/interfaces/images/plugins/exif.py +1103 -0
  230. parrot/interfaces/images/plugins/hash.py +52 -0
  231. parrot/interfaces/images/plugins/vision.py +104 -0
  232. parrot/interfaces/images/plugins/yolo.py +66 -0
  233. parrot/interfaces/images/plugins/zerodetect.py +197 -0
  234. parrot/interfaces/o365.py +978 -0
  235. parrot/interfaces/onedrive.py +822 -0
  236. parrot/interfaces/sharepoint.py +1435 -0
  237. parrot/interfaces/soap.py +257 -0
  238. parrot/loaders/__init__.py +8 -0
  239. parrot/loaders/abstract.py +1131 -0
  240. parrot/loaders/audio.py +199 -0
  241. parrot/loaders/basepdf.py +53 -0
  242. parrot/loaders/basevideo.py +1568 -0
  243. parrot/loaders/csv.py +409 -0
  244. parrot/loaders/docx.py +116 -0
  245. parrot/loaders/epubloader.py +316 -0
  246. parrot/loaders/excel.py +199 -0
  247. parrot/loaders/factory.py +55 -0
  248. parrot/loaders/files/__init__.py +0 -0
  249. parrot/loaders/files/abstract.py +39 -0
  250. parrot/loaders/files/html.py +26 -0
  251. parrot/loaders/files/text.py +63 -0
  252. parrot/loaders/html.py +152 -0
  253. parrot/loaders/markdown.py +442 -0
  254. parrot/loaders/pdf.py +373 -0
  255. parrot/loaders/pdfmark.py +320 -0
  256. parrot/loaders/pdftables.py +506 -0
  257. parrot/loaders/ppt.py +476 -0
  258. parrot/loaders/qa.py +63 -0
  259. parrot/loaders/splitters/__init__.py +10 -0
  260. parrot/loaders/splitters/base.py +138 -0
  261. parrot/loaders/splitters/md.py +228 -0
  262. parrot/loaders/splitters/token.py +143 -0
  263. parrot/loaders/txt.py +26 -0
  264. parrot/loaders/video.py +89 -0
  265. parrot/loaders/videolocal.py +218 -0
  266. parrot/loaders/videounderstanding.py +377 -0
  267. parrot/loaders/vimeo.py +167 -0
  268. parrot/loaders/web.py +599 -0
  269. parrot/loaders/youtube.py +504 -0
  270. parrot/manager/__init__.py +5 -0
  271. parrot/manager/manager.py +1030 -0
  272. parrot/mcp/__init__.py +28 -0
  273. parrot/mcp/adapter.py +105 -0
  274. parrot/mcp/cli.py +174 -0
  275. parrot/mcp/client.py +119 -0
  276. parrot/mcp/config.py +75 -0
  277. parrot/mcp/integration.py +842 -0
  278. parrot/mcp/oauth.py +933 -0
  279. parrot/mcp/server.py +225 -0
  280. parrot/mcp/transports/__init__.py +3 -0
  281. parrot/mcp/transports/base.py +279 -0
  282. parrot/mcp/transports/grpc_session.py +163 -0
  283. parrot/mcp/transports/http.py +312 -0
  284. parrot/mcp/transports/mcp.proto +108 -0
  285. parrot/mcp/transports/quic.py +1082 -0
  286. parrot/mcp/transports/sse.py +330 -0
  287. parrot/mcp/transports/stdio.py +309 -0
  288. parrot/mcp/transports/unix.py +395 -0
  289. parrot/mcp/transports/websocket.py +547 -0
  290. parrot/memory/__init__.py +16 -0
  291. parrot/memory/abstract.py +209 -0
  292. parrot/memory/agent.py +32 -0
  293. parrot/memory/cache.py +175 -0
  294. parrot/memory/core.py +555 -0
  295. parrot/memory/file.py +153 -0
  296. parrot/memory/mem.py +131 -0
  297. parrot/memory/redis.py +613 -0
  298. parrot/models/__init__.py +46 -0
  299. parrot/models/basic.py +118 -0
  300. parrot/models/compliance.py +208 -0
  301. parrot/models/crew.py +395 -0
  302. parrot/models/detections.py +654 -0
  303. parrot/models/generation.py +85 -0
  304. parrot/models/google.py +223 -0
  305. parrot/models/groq.py +23 -0
  306. parrot/models/openai.py +30 -0
  307. parrot/models/outputs.py +285 -0
  308. parrot/models/responses.py +938 -0
  309. parrot/notifications/__init__.py +743 -0
  310. parrot/openapi/__init__.py +3 -0
  311. parrot/openapi/components.yaml +641 -0
  312. parrot/openapi/config.py +322 -0
  313. parrot/outputs/__init__.py +32 -0
  314. parrot/outputs/formats/__init__.py +108 -0
  315. parrot/outputs/formats/altair.py +359 -0
  316. parrot/outputs/formats/application.py +122 -0
  317. parrot/outputs/formats/base.py +351 -0
  318. parrot/outputs/formats/bokeh.py +356 -0
  319. parrot/outputs/formats/card.py +424 -0
  320. parrot/outputs/formats/chart.py +436 -0
  321. parrot/outputs/formats/d3.py +255 -0
  322. parrot/outputs/formats/echarts.py +310 -0
  323. parrot/outputs/formats/generators/__init__.py +0 -0
  324. parrot/outputs/formats/generators/abstract.py +61 -0
  325. parrot/outputs/formats/generators/panel.py +145 -0
  326. parrot/outputs/formats/generators/streamlit.py +86 -0
  327. parrot/outputs/formats/generators/terminal.py +63 -0
  328. parrot/outputs/formats/holoviews.py +310 -0
  329. parrot/outputs/formats/html.py +147 -0
  330. parrot/outputs/formats/jinja2.py +46 -0
  331. parrot/outputs/formats/json.py +87 -0
  332. parrot/outputs/formats/map.py +933 -0
  333. parrot/outputs/formats/markdown.py +172 -0
  334. parrot/outputs/formats/matplotlib.py +237 -0
  335. parrot/outputs/formats/mixins/__init__.py +0 -0
  336. parrot/outputs/formats/mixins/emaps.py +855 -0
  337. parrot/outputs/formats/plotly.py +341 -0
  338. parrot/outputs/formats/seaborn.py +310 -0
  339. parrot/outputs/formats/table.py +397 -0
  340. parrot/outputs/formats/template_report.py +138 -0
  341. parrot/outputs/formats/yaml.py +125 -0
  342. parrot/outputs/formatter.py +152 -0
  343. parrot/outputs/templates/__init__.py +95 -0
  344. parrot/pipelines/__init__.py +0 -0
  345. parrot/pipelines/abstract.py +210 -0
  346. parrot/pipelines/detector.py +124 -0
  347. parrot/pipelines/models.py +90 -0
  348. parrot/pipelines/planogram.py +3002 -0
  349. parrot/pipelines/table.sql +97 -0
  350. parrot/plugins/__init__.py +106 -0
  351. parrot/plugins/importer.py +80 -0
  352. parrot/py.typed +0 -0
  353. parrot/registry/__init__.py +18 -0
  354. parrot/registry/registry.py +594 -0
  355. parrot/scheduler/__init__.py +1189 -0
  356. parrot/scheduler/models.py +60 -0
  357. parrot/security/__init__.py +16 -0
  358. parrot/security/prompt_injection.py +268 -0
  359. parrot/security/security_events.sql +25 -0
  360. parrot/services/__init__.py +1 -0
  361. parrot/services/mcp/__init__.py +8 -0
  362. parrot/services/mcp/config.py +13 -0
  363. parrot/services/mcp/server.py +295 -0
  364. parrot/services/o365_remote_auth.py +235 -0
  365. parrot/stores/__init__.py +7 -0
  366. parrot/stores/abstract.py +352 -0
  367. parrot/stores/arango.py +1090 -0
  368. parrot/stores/bigquery.py +1377 -0
  369. parrot/stores/cache.py +106 -0
  370. parrot/stores/empty.py +10 -0
  371. parrot/stores/faiss_store.py +1157 -0
  372. parrot/stores/kb/__init__.py +9 -0
  373. parrot/stores/kb/abstract.py +68 -0
  374. parrot/stores/kb/cache.py +165 -0
  375. parrot/stores/kb/doc.py +325 -0
  376. parrot/stores/kb/hierarchy.py +346 -0
  377. parrot/stores/kb/local.py +457 -0
  378. parrot/stores/kb/prompt.py +28 -0
  379. parrot/stores/kb/redis.py +659 -0
  380. parrot/stores/kb/store.py +115 -0
  381. parrot/stores/kb/user.py +374 -0
  382. parrot/stores/models.py +59 -0
  383. parrot/stores/pgvector.py +3 -0
  384. parrot/stores/postgres.py +2853 -0
  385. parrot/stores/utils/__init__.py +0 -0
  386. parrot/stores/utils/chunking.py +197 -0
  387. parrot/telemetry/__init__.py +3 -0
  388. parrot/telemetry/mixin.py +111 -0
  389. parrot/template/__init__.py +3 -0
  390. parrot/template/engine.py +259 -0
  391. parrot/tools/__init__.py +23 -0
  392. parrot/tools/abstract.py +644 -0
  393. parrot/tools/agent.py +363 -0
  394. parrot/tools/arangodbsearch.py +537 -0
  395. parrot/tools/arxiv_tool.py +188 -0
  396. parrot/tools/calculator/__init__.py +3 -0
  397. parrot/tools/calculator/operations/__init__.py +38 -0
  398. parrot/tools/calculator/operations/calculus.py +80 -0
  399. parrot/tools/calculator/operations/statistics.py +76 -0
  400. parrot/tools/calculator/tool.py +150 -0
  401. parrot/tools/cloudwatch.py +988 -0
  402. parrot/tools/codeinterpreter/__init__.py +127 -0
  403. parrot/tools/codeinterpreter/executor.py +371 -0
  404. parrot/tools/codeinterpreter/internals.py +473 -0
  405. parrot/tools/codeinterpreter/models.py +643 -0
  406. parrot/tools/codeinterpreter/prompts.py +224 -0
  407. parrot/tools/codeinterpreter/tool.py +664 -0
  408. parrot/tools/company_info/__init__.py +6 -0
  409. parrot/tools/company_info/tool.py +1138 -0
  410. parrot/tools/correlationanalysis.py +437 -0
  411. parrot/tools/database/abstract.py +286 -0
  412. parrot/tools/database/bq.py +115 -0
  413. parrot/tools/database/cache.py +284 -0
  414. parrot/tools/database/models.py +95 -0
  415. parrot/tools/database/pg.py +343 -0
  416. parrot/tools/databasequery.py +1159 -0
  417. parrot/tools/db.py +1800 -0
  418. parrot/tools/ddgo.py +370 -0
  419. parrot/tools/decorators.py +271 -0
  420. parrot/tools/dftohtml.py +282 -0
  421. parrot/tools/document.py +549 -0
  422. parrot/tools/ecs.py +819 -0
  423. parrot/tools/edareport.py +368 -0
  424. parrot/tools/elasticsearch.py +1049 -0
  425. parrot/tools/employees.py +462 -0
  426. parrot/tools/epson/__init__.py +96 -0
  427. parrot/tools/excel.py +683 -0
  428. parrot/tools/file/__init__.py +13 -0
  429. parrot/tools/file/abstract.py +76 -0
  430. parrot/tools/file/gcs.py +378 -0
  431. parrot/tools/file/local.py +284 -0
  432. parrot/tools/file/s3.py +511 -0
  433. parrot/tools/file/tmp.py +309 -0
  434. parrot/tools/file/tool.py +501 -0
  435. parrot/tools/file_reader.py +129 -0
  436. parrot/tools/flowtask/__init__.py +19 -0
  437. parrot/tools/flowtask/tool.py +761 -0
  438. parrot/tools/gittoolkit.py +508 -0
  439. parrot/tools/google/__init__.py +18 -0
  440. parrot/tools/google/base.py +169 -0
  441. parrot/tools/google/tools.py +1251 -0
  442. parrot/tools/googlelocation.py +5 -0
  443. parrot/tools/googleroutes.py +5 -0
  444. parrot/tools/googlesearch.py +5 -0
  445. parrot/tools/googlesitesearch.py +5 -0
  446. parrot/tools/googlevoice.py +2 -0
  447. parrot/tools/gvoice.py +695 -0
  448. parrot/tools/ibisworld/README.md +225 -0
  449. parrot/tools/ibisworld/__init__.py +11 -0
  450. parrot/tools/ibisworld/tool.py +366 -0
  451. parrot/tools/jiratoolkit.py +1718 -0
  452. parrot/tools/manager.py +1098 -0
  453. parrot/tools/math.py +152 -0
  454. parrot/tools/metadata.py +476 -0
  455. parrot/tools/msteams.py +1621 -0
  456. parrot/tools/msword.py +635 -0
  457. parrot/tools/multidb.py +580 -0
  458. parrot/tools/multistoresearch.py +369 -0
  459. parrot/tools/networkninja.py +167 -0
  460. parrot/tools/nextstop/__init__.py +4 -0
  461. parrot/tools/nextstop/base.py +286 -0
  462. parrot/tools/nextstop/employee.py +733 -0
  463. parrot/tools/nextstop/store.py +462 -0
  464. parrot/tools/notification.py +435 -0
  465. parrot/tools/o365/__init__.py +42 -0
  466. parrot/tools/o365/base.py +295 -0
  467. parrot/tools/o365/bundle.py +522 -0
  468. parrot/tools/o365/events.py +554 -0
  469. parrot/tools/o365/mail.py +992 -0
  470. parrot/tools/o365/onedrive.py +497 -0
  471. parrot/tools/o365/sharepoint.py +641 -0
  472. parrot/tools/openapi_toolkit.py +904 -0
  473. parrot/tools/openweather.py +527 -0
  474. parrot/tools/pdfprint.py +1001 -0
  475. parrot/tools/powerbi.py +518 -0
  476. parrot/tools/powerpoint.py +1113 -0
  477. parrot/tools/pricestool.py +146 -0
  478. parrot/tools/products/__init__.py +246 -0
  479. parrot/tools/prophet_tool.py +171 -0
  480. parrot/tools/pythonpandas.py +630 -0
  481. parrot/tools/pythonrepl.py +910 -0
  482. parrot/tools/qsource.py +436 -0
  483. parrot/tools/querytoolkit.py +395 -0
  484. parrot/tools/quickeda.py +827 -0
  485. parrot/tools/resttool.py +553 -0
  486. parrot/tools/retail/__init__.py +0 -0
  487. parrot/tools/retail/bby.py +528 -0
  488. parrot/tools/sandboxtool.py +703 -0
  489. parrot/tools/sassie/__init__.py +352 -0
  490. parrot/tools/scraping/__init__.py +7 -0
  491. parrot/tools/scraping/docs/select.md +466 -0
  492. parrot/tools/scraping/documentation.md +1278 -0
  493. parrot/tools/scraping/driver.py +436 -0
  494. parrot/tools/scraping/models.py +576 -0
  495. parrot/tools/scraping/options.py +85 -0
  496. parrot/tools/scraping/orchestrator.py +517 -0
  497. parrot/tools/scraping/readme.md +740 -0
  498. parrot/tools/scraping/tool.py +3115 -0
  499. parrot/tools/seasonaldetection.py +642 -0
  500. parrot/tools/shell_tool/__init__.py +5 -0
  501. parrot/tools/shell_tool/actions.py +408 -0
  502. parrot/tools/shell_tool/engine.py +155 -0
  503. parrot/tools/shell_tool/models.py +322 -0
  504. parrot/tools/shell_tool/tool.py +442 -0
  505. parrot/tools/site_search.py +214 -0
  506. parrot/tools/textfile.py +418 -0
  507. parrot/tools/think.py +378 -0
  508. parrot/tools/toolkit.py +298 -0
  509. parrot/tools/webapp_tool.py +187 -0
  510. parrot/tools/whatif.py +1279 -0
  511. parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
  512. parrot/tools/workday/__init__.py +6 -0
  513. parrot/tools/workday/models.py +1389 -0
  514. parrot/tools/workday/tool.py +1293 -0
  515. parrot/tools/yfinance_tool.py +306 -0
  516. parrot/tools/zipcode.py +217 -0
  517. parrot/utils/__init__.py +2 -0
  518. parrot/utils/helpers.py +73 -0
  519. parrot/utils/parsers/__init__.py +5 -0
  520. parrot/utils/parsers/toml.c +12078 -0
  521. parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
  522. parrot/utils/parsers/toml.pyx +21 -0
  523. parrot/utils/toml.py +11 -0
  524. parrot/utils/types.cpp +20936 -0
  525. parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
  526. parrot/utils/types.pyx +213 -0
  527. parrot/utils/uv.py +11 -0
  528. parrot/version.py +10 -0
  529. parrot/yaml-rs/Cargo.lock +350 -0
  530. parrot/yaml-rs/Cargo.toml +19 -0
  531. parrot/yaml-rs/pyproject.toml +19 -0
  532. parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
  533. parrot/yaml-rs/src/lib.rs +222 -0
  534. requirements/docker-compose.yml +24 -0
  535. 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