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
parrot/memory/redis.py ADDED
@@ -0,0 +1,613 @@
1
+ from typing import List, Dict, Any, Optional
2
+ from datetime import datetime
3
+ import json
4
+ from redis.asyncio import Redis
5
+ from datamodel.parsers.json import json_encoder, json_decoder # pylint: disable=E0611 # noqa
6
+ from .abstract import ConversationMemory, ConversationHistory, ConversationTurn
7
+ from ..conf import REDIS_HISTORY_URL
8
+
9
+
10
+ class RedisConversation(ConversationMemory):
11
+ """Redis-based conversation memory with proper encoding handling."""
12
+
13
+ def __init__(
14
+ self,
15
+ redis_url: str = None,
16
+ key_prefix: str = "conversation",
17
+ use_hash_storage: bool = True
18
+ ):
19
+ self.redis_url = redis_url or REDIS_HISTORY_URL
20
+ self.key_prefix = key_prefix
21
+ self.use_hash_storage = use_hash_storage
22
+ self.redis = Redis.from_url(
23
+ self.redis_url,
24
+ decode_responses=True,
25
+ encoding="utf-8",
26
+ socket_connect_timeout=5,
27
+ socket_timeout=5,
28
+ retry_on_timeout=True
29
+ )
30
+
31
+ def _get_key(
32
+ self,
33
+ user_id: str,
34
+ session_id: str,
35
+ chatbot_id: Optional[str] = None
36
+ ) -> str:
37
+ """Generate Redis key for conversation history."""
38
+ parts = [self.key_prefix]
39
+ if chatbot_id:
40
+ parts.append(str(chatbot_id))
41
+ parts.extend([user_id, session_id])
42
+ return ":".join(parts)
43
+
44
+ def _get_user_sessions_key(
45
+ self,
46
+ user_id: str,
47
+ chatbot_id: Optional[str] = None
48
+ ) -> str:
49
+ """Generate Redis key for user's session list."""
50
+ parts = [f"{self.key_prefix}_sessions"]
51
+ if chatbot_id:
52
+ parts.append(str(chatbot_id))
53
+ parts.append(user_id)
54
+ return ":".join(parts)
55
+
56
+ def _serialize_data(self, data: Any) -> str:
57
+ """Serialize data to JSON string with proper encoding."""
58
+ try:
59
+ # Use standard json module with specific settings to avoid encoding issues
60
+ return json.dumps(data, ensure_ascii=False, separators=(',', ':'), default=str)
61
+ except Exception as e:
62
+ print(f"Serialization error: {e}")
63
+ # Fallback to your custom encoder
64
+ return json_encoder(data)
65
+
66
+ def _deserialize_data(self, data: str) -> Any:
67
+ """Deserialize JSON string to Python object."""
68
+ try:
69
+ # Use standard json module first
70
+ return json.loads(data)
71
+ except Exception as e:
72
+ print(f"Deserialization error with standard json: {e}")
73
+ # Fallback to your custom decoder
74
+ try:
75
+ # Fallback to your custom decoder
76
+ return json_decoder(data)
77
+ except Exception as e2:
78
+ print(f"Deserialization error with custom decoder: {e2}")
79
+ print(f"Problematic data (first 200 chars): {data[:200]}")
80
+ return None
81
+
82
+ async def create_history(
83
+ self,
84
+ user_id: str,
85
+ session_id: str,
86
+ chatbot_id: Optional[str] = None,
87
+ metadata: Optional[Dict[str, Any]] = None,
88
+ ) -> ConversationHistory:
89
+ """Create a new conversation history."""
90
+ history = ConversationHistory(
91
+ session_id=session_id,
92
+ user_id=user_id,
93
+ chatbot_id=chatbot_id,
94
+ metadata=metadata or {}
95
+ )
96
+
97
+ if self.use_hash_storage:
98
+ # Method 1: Using Redis Hash (RECOMMENDED for objects)
99
+ key = self._get_key(user_id, session_id, chatbot_id)
100
+ history_dict = history.to_dict()
101
+
102
+ # Store each field separately in a hash
103
+ mapping = {
104
+ 'session_id': history_dict['session_id'],
105
+ 'user_id': history_dict['user_id'],
106
+ 'chatbot_id': chatbot_id,
107
+ 'turns': self._serialize_data(history_dict['turns']),
108
+ 'created_at': history_dict['created_at'],
109
+ 'updated_at': history_dict['updated_at'],
110
+ 'metadata': self._serialize_data(history_dict['metadata'])
111
+ }
112
+ if history_dict.get('chatbot_id') is not None:
113
+ mapping['chatbot_id'] = history_dict['chatbot_id']
114
+ await self.redis.hset(key, mapping=mapping)
115
+ else:
116
+ # Method 2: Using simple key-value storage
117
+ key = self._get_key(user_id, session_id, chatbot_id)
118
+ serialized_data = self._serialize_data(history.to_dict())
119
+ await self.redis.set(key, serialized_data)
120
+
121
+ # Add to user sessions set
122
+ await self.redis.sadd(
123
+ self._get_user_sessions_key(user_id, chatbot_id),
124
+ session_id
125
+ )
126
+ return history
127
+
128
+ async def get_history(
129
+ self,
130
+ user_id: str,
131
+ session_id: str,
132
+ chatbot_id: Optional[str] = None
133
+ ) -> Optional[ConversationHistory]:
134
+ """Get a conversation history."""
135
+ key = self._get_key(user_id, session_id, chatbot_id)
136
+
137
+ if self.use_hash_storage:
138
+ # Method 1: Get from Redis Hash
139
+ data = await self.redis.hgetall(key)
140
+ if not data:
141
+ return None
142
+
143
+ try:
144
+ # Reconstruct the history dict
145
+ history_dict = {
146
+ 'session_id': data['session_id'],
147
+ 'user_id': data['user_id'],
148
+ 'chatbot_id': data.get('chatbot_id', chatbot_id),
149
+ 'turns': self._deserialize_data(data['turns']),
150
+ 'created_at': data['created_at'],
151
+ 'updated_at': data['updated_at'],
152
+ 'metadata': self._deserialize_data(data['metadata'])
153
+ }
154
+ return ConversationHistory.from_dict(history_dict)
155
+ except (KeyError, ValueError) as e:
156
+ print(f"Error deserializing conversation history: {e}")
157
+ return None
158
+ else:
159
+ # Method 2: Get from simple key-value
160
+ data = await self.redis.get(key)
161
+ if data:
162
+ try:
163
+ history_dict = self._deserialize_data(data)
164
+ if history_dict is not None and chatbot_id and not history_dict.get('chatbot_id'):
165
+ history_dict['chatbot_id'] = chatbot_id
166
+ return ConversationHistory.from_dict(history_dict)
167
+ except (ValueError, KeyError) as e:
168
+ print(f"Error deserializing conversation history: {e}")
169
+ return None
170
+ return None
171
+
172
+ async def update_history(self, history: ConversationHistory) -> None:
173
+ """Update a conversation history."""
174
+ key = self._get_key(history.user_id, history.session_id, history.chatbot_id)
175
+
176
+ if self.use_hash_storage:
177
+ # Method 1: Update Redis Hash
178
+ history_dict = history.to_dict()
179
+ mapping = {
180
+ 'session_id': history_dict['session_id'],
181
+ 'user_id': history_dict['user_id'],
182
+ 'turns': self._serialize_data(history_dict['turns']),
183
+ 'created_at': history_dict['created_at'],
184
+ 'updated_at': history_dict['updated_at'],
185
+ 'metadata': self._serialize_data(history_dict['metadata'])
186
+ }
187
+ if history_dict.get('chatbot_id') is not None:
188
+ mapping['chatbot_id'] = history_dict['chatbot_id']
189
+ await self.redis.hset(key, mapping=mapping)
190
+ else:
191
+ # Method 2: Update simple key-value
192
+ serialized_data = self._serialize_data(history.to_dict())
193
+ await self.redis.set(key, serialized_data)
194
+
195
+ async def add_turn(
196
+ self,
197
+ user_id: str,
198
+ session_id: str,
199
+ turn: ConversationTurn,
200
+ chatbot_id: Optional[str] = None
201
+ ) -> None:
202
+ """Add a turn to the conversation efficiently."""
203
+ if self.use_hash_storage:
204
+ # Optimized: Only update the turns field
205
+ key = self._get_key(user_id, session_id, chatbot_id)
206
+
207
+ # Get current turns
208
+ current_turns_data = await self.redis.hget(key, 'turns')
209
+ if current_turns_data:
210
+ turns = self._deserialize_data(current_turns_data)
211
+ else:
212
+ turns = []
213
+
214
+ # Add new turn
215
+ turns.append(turn.to_dict())
216
+
217
+ # Update only the turns and updated_at fields
218
+ mapping = {
219
+ 'turns': self._serialize_data(turns),
220
+ 'updated_at': datetime.now().isoformat()
221
+ }
222
+ if chatbot_id is not None:
223
+ mapping['chatbot_id'] = str(chatbot_id)
224
+ await self.redis.hset(key, mapping=mapping)
225
+ else:
226
+ # Fallback to full history update
227
+ history = await self.get_history(user_id, session_id, chatbot_id)
228
+ if history:
229
+ history.add_turn(turn)
230
+ await self.update_history(history)
231
+
232
+ async def clear_history(
233
+ self,
234
+ user_id: str,
235
+ session_id: str,
236
+ chatbot_id: Optional[str] = None
237
+ ) -> None:
238
+ """Clear a conversation history."""
239
+ if self.use_hash_storage:
240
+ # Optimized: Only clear turns
241
+ key = self._get_key(user_id, session_id, chatbot_id)
242
+ # Reset turns to empty list and update updated_at
243
+ mapping = {
244
+ 'turns': self._serialize_data([]),
245
+ 'updated_at': datetime.now().isoformat()
246
+ }
247
+ if chatbot_id is not None:
248
+ mapping['chatbot_id'] = str(chatbot_id)
249
+ await self.redis.hset(key, mapping=mapping)
250
+ else:
251
+ history = await self.get_history(user_id, session_id, chatbot_id)
252
+ if history:
253
+ history.clear_turns()
254
+ await self.update_history(history)
255
+
256
+ async def list_sessions(
257
+ self,
258
+ user_id: str,
259
+ chatbot_id: Optional[str] = None
260
+ ) -> List[str]:
261
+ """List all session IDs for a user."""
262
+ sessions = await self.redis.smembers(
263
+ self._get_user_sessions_key(user_id, chatbot_id)
264
+ )
265
+ # Since decode_responses=True, sessions should already be strings
266
+ return list(sessions)
267
+
268
+ async def delete_history(
269
+ self,
270
+ user_id: str,
271
+ session_id: str,
272
+ chatbot_id: Optional[str] = None
273
+ ) -> bool:
274
+ """Delete a conversation history entirely."""
275
+ key = self._get_key(user_id, session_id, chatbot_id)
276
+ result = await self.redis.delete(key)
277
+ await self.redis.srem(
278
+ self._get_user_sessions_key(user_id, chatbot_id),
279
+ session_id
280
+ )
281
+ return result > 0
282
+
283
+ async def close(self):
284
+ """Close the Redis connection."""
285
+ try:
286
+ await self.redis.close()
287
+ except Exception as e:
288
+ self.logger.error(f"Error closing Redis connection: {e}")
289
+
290
+ async def ping(self) -> bool:
291
+ """Test Redis connection."""
292
+ try:
293
+ await self.redis.ping()
294
+ return True
295
+ except Exception as e:
296
+ self.logger.error(f"Error pinging Redis: {e}")
297
+ return False
298
+
299
+ # Additional utility methods for debugging
300
+ async def get_raw_data(
301
+ self,
302
+ user_id: str,
303
+ session_id: str,
304
+ chatbot_id: Optional[str] = None
305
+ ) -> Optional[Dict]:
306
+ """Get raw data from Redis for debugging."""
307
+ key = self._get_key(user_id, session_id, chatbot_id)
308
+
309
+ if self.use_hash_storage:
310
+ return await self.redis.hgetall(key)
311
+ data = await self.redis.get(key)
312
+ return {"raw_data": data} if data else None
313
+
314
+ async def debug_conversation(
315
+ self,
316
+ user_id: str,
317
+ session_id: str,
318
+ chatbot_id: Optional[str] = None
319
+ ) -> Dict[str, Any]:
320
+ """Debug method to inspect conversation data."""
321
+ raw_data = await self.get_raw_data(user_id, session_id, chatbot_id)
322
+ history = await self.get_history(user_id, session_id, chatbot_id)
323
+
324
+ return {
325
+ "raw_data": raw_data,
326
+ "parsed_history": history.to_dict() if history else None,
327
+ "turns_count": len(history.turns) if history else 0,
328
+ "storage_method": "hash" if self.use_hash_storage else "string"
329
+ }
330
+
331
+ async def list_sessions_by_chatbot(
332
+ self,
333
+ chatbot_id: str,
334
+ user_id: Optional[str] = None
335
+ ) -> List[str]:
336
+ """List all sessions for a specific chatbot.
337
+
338
+ Args:
339
+ chatbot_id: The chatbot identifier
340
+ user_id: Optional user filter
341
+
342
+ Returns:
343
+ List of session IDs
344
+ """
345
+ if user_id:
346
+ # Get sessions for specific user and chatbot
347
+ return await self.list_sessions(user_id, chatbot_id)
348
+
349
+ # Get all sessions for this chatbot across all users
350
+ pattern = f"{self.key_prefix}:{chatbot_id}:*"
351
+ sessions = []
352
+ cursor = 0
353
+
354
+ while True:
355
+ cursor, keys = await self.redis.scan(
356
+ cursor,
357
+ match=pattern,
358
+ count=100
359
+ )
360
+ for key in keys:
361
+ # Extract session_id from key
362
+ # Format: conversation:chatbot_id:user_id:session_id
363
+ parts = key.split(':')
364
+ if len(parts) >= 4:
365
+ sessions.append(parts[3])
366
+
367
+ if cursor == 0:
368
+ break
369
+
370
+ return sessions
371
+
372
+
373
+ async def get_chatbot_stats(self, chatbot_id: str) -> Dict[str, Any]:
374
+ """Get statistics for a specific chatbot.
375
+
376
+ Returns:
377
+ Dictionary with conversation counts, active users, etc.
378
+ """
379
+ pattern = f"{self.key_prefix}:{chatbot_id}:*"
380
+ total_conversations = 0
381
+ total_turns = 0
382
+ unique_users = set()
383
+ cursor = 0
384
+
385
+ while True:
386
+ cursor, keys = await self.redis.scan(
387
+ cursor,
388
+ match=pattern,
389
+ count=100
390
+ )
391
+ total_conversations += len(keys)
392
+
393
+ for key in keys:
394
+ # Extract user_id
395
+ parts = key.split(':')
396
+ if len(parts) >= 3:
397
+ unique_users.add(parts[2])
398
+
399
+ # Count turns
400
+ if self.use_hash_storage:
401
+ turns_data = await self.redis.hget(key, 'turns')
402
+ if turns_data:
403
+ turns = self._deserialize_data(turns_data)
404
+ total_turns += len(turns)
405
+
406
+ if cursor == 0:
407
+ break
408
+
409
+ return {
410
+ 'chatbot_id': chatbot_id,
411
+ 'total_conversations': total_conversations,
412
+ 'total_turns': total_turns,
413
+ 'unique_users': len(unique_users),
414
+ 'avg_turns_per_conversation': total_turns / total_conversations if total_conversations > 0 else 0
415
+ }
416
+
417
+
418
+ async def delete_all_chatbot_conversations(
419
+ self,
420
+ chatbot_id: str,
421
+ user_id: Optional[str] = None
422
+ ) -> int:
423
+ """Delete all conversations for a chatbot.
424
+
425
+ Args:
426
+ chatbot_id: The chatbot identifier
427
+ user_id: Optional user filter
428
+
429
+ Returns:
430
+ Number of conversations deleted
431
+ """
432
+ if user_id:
433
+ pattern = f"{self.key_prefix}:{chatbot_id}:{user_id}:*"
434
+ else:
435
+ pattern = f"{self.key_prefix}:{chatbot_id}:*"
436
+
437
+ deleted_count = 0
438
+ cursor = 0
439
+
440
+ while True:
441
+ cursor, keys = await self.redis.scan(
442
+ cursor,
443
+ match=pattern,
444
+ count=100
445
+ )
446
+
447
+ if keys:
448
+ deleted_count += await self.redis.delete(*keys)
449
+
450
+ if cursor == 0:
451
+ break
452
+
453
+ # Also clean up session sets
454
+ if user_id:
455
+ await self.redis.delete(self._get_user_sessions_key(user_id, chatbot_id))
456
+ else:
457
+ # Clean all user session sets for this chatbot
458
+ session_pattern = f"{self.key_prefix}_sessions:{chatbot_id}:*"
459
+ cursor = 0
460
+ while True:
461
+ cursor, session_keys = await self.redis.scan(
462
+ cursor,
463
+ match=session_pattern,
464
+ count=100
465
+ )
466
+ if session_keys:
467
+ await self.redis.delete(*session_keys)
468
+ if cursor == 0:
469
+ break
470
+
471
+ return deleted_count
472
+
473
+ async def _update_chatbot_index(
474
+ self,
475
+ chatbot_id: str,
476
+ user_id: str,
477
+ session_id: str,
478
+ operation: str = 'add'
479
+ ) -> None:
480
+ """Maintain reverse index for fast chatbot queries.
481
+
482
+ Args:
483
+ chatbot_id: The chatbot identifier
484
+ user_id: The user identifier
485
+ session_id: The session identifier
486
+ operation: 'add' or 'remove'
487
+ """
488
+ # Index: all users who interacted with this chatbot
489
+ users_key = f"{self.key_prefix}_index:chatbot_users:{chatbot_id}"
490
+
491
+ if operation == 'add':
492
+ await self.redis.sadd(users_key, user_id)
493
+ # Set expiry if needed
494
+ # await self.redis.expire(users_key, 86400 * 30) # 30 days
495
+ elif operation == 'remove':
496
+ # Check if user has any other sessions with this chatbot
497
+ sessions = await self.list_sessions(user_id, chatbot_id)
498
+ if not sessions:
499
+ await self.redis.srem(users_key, user_id)
500
+
501
+
502
+ async def get_chatbot_users(self, chatbot_id: str) -> List[str]:
503
+ """Get all users who have interacted with a chatbot."""
504
+ users_key = f"{self.key_prefix}_index:chatbot_users:{chatbot_id}"
505
+ users = await self.redis.smembers(users_key)
506
+ return list(users)
507
+
508
+ # Example usage and testing
509
+ async def test_redis_conversation():
510
+ """Enhanced test with multiple chatbots and users."""
511
+ redis_memory = RedisConversation(use_hash_storage=True)
512
+
513
+ if not await redis_memory.ping():
514
+ print("Redis connection failed!")
515
+ return
516
+
517
+ chatbot1 = "sales_bot"
518
+ chatbot2 = "support_bot"
519
+ user1 = "user_alice"
520
+ user2 = "user_bob"
521
+
522
+ try:
523
+ # Test 1: Multiple bots with same user
524
+ print("\n=== Test 1: Same user, different bots ===")
525
+ h1 = await redis_memory.create_history(user1, "session1", chatbot1)
526
+ h2 = await redis_memory.create_history(user1, "session2", chatbot2)
527
+
528
+ print(f"Created sessions: {h1.session_id}, {h2.session_id}")
529
+
530
+ # Test 2: List sessions by chatbot
531
+ print("\n=== Test 2: List sessions by chatbot ===")
532
+ sessions1 = await redis_memory.list_sessions(user1, chatbot1)
533
+ sessions2 = await redis_memory.list_sessions(user1, chatbot2)
534
+
535
+ print(f"User {user1} sessions with {chatbot1}: {sessions1}")
536
+ print(f"User {user1} sessions with {chatbot2}: {sessions2}")
537
+
538
+ # Test 3: Add turns to different bots
539
+ print("\n=== Test 3: Add turns ===")
540
+ turn1 = ConversationTurn(
541
+ turn_id="t1",
542
+ user_id=user1,
543
+ user_message="What's your price?",
544
+ assistant_response="Our starting price is $99/month."
545
+ )
546
+
547
+ turn2 = ConversationTurn(
548
+ turn_id="t2",
549
+ user_id=user1,
550
+ user_message="I need help",
551
+ assistant_response="How can I assist you today?"
552
+ )
553
+
554
+ await redis_memory.add_turn(user1, "session1", turn1, chatbot1)
555
+ await redis_memory.add_turn(user1, "session2", turn2, chatbot2)
556
+
557
+ # Test 4: Retrieve and verify isolation
558
+ print("\n=== Test 4: Verify conversation isolation ===")
559
+ conv1 = await redis_memory.get_history(user1, "session1", chatbot1)
560
+ conv2 = await redis_memory.get_history(user1, "session2", chatbot2)
561
+
562
+ print(f"Sales bot conversation: {conv1.turns[0].assistant_response}")
563
+ print(f"Support bot conversation: {conv2.turns[0].assistant_response}")
564
+
565
+ # Test 5: Cross-contamination check
566
+ print("\n=== Test 5: Cross-contamination check ===")
567
+ wrong_bot = await redis_memory.get_history(user1, "session1", chatbot2)
568
+ print(f"Trying to access sales session with support bot ID: {wrong_bot}") # Should be None
569
+
570
+ # Test 6: Same session_id, different chatbots
571
+ print("\n=== Test 6: Session ID collision test ===")
572
+ h3 = await redis_memory.create_history(user2, "common_session", chatbot1)
573
+ h4 = await redis_memory.create_history(user2, "common_session", chatbot2)
574
+
575
+ turn3 = ConversationTurn(
576
+ turn_id="t3",
577
+ user_id=user2,
578
+ user_message="Hello sales",
579
+ assistant_response="Welcome to sales!"
580
+ )
581
+
582
+ turn4 = ConversationTurn(
583
+ turn_id="t4",
584
+ user_id=user2,
585
+ user_message="Hello support",
586
+ assistant_response="Welcome to support!"
587
+ )
588
+
589
+ await redis_memory.add_turn(user2, "common_session", turn3, chatbot1)
590
+ await redis_memory.add_turn(user2, "common_session", turn4, chatbot2)
591
+
592
+ c1 = await redis_memory.get_history(user2, "common_session", chatbot1)
593
+ c2 = await redis_memory.get_history(user2, "common_session", chatbot2)
594
+
595
+ print(f"Sales bot (common session): {c1.turns[0].assistant_response}")
596
+ print(f"Support bot (common session): {c2.turns[0].assistant_response}")
597
+
598
+ # Cleanup
599
+ print("\n=== Cleanup ===")
600
+ await redis_memory.delete_history(user1, "session1", chatbot1)
601
+ await redis_memory.delete_history(user1, "session2", chatbot2)
602
+ await redis_memory.delete_history(user2, "common_session", chatbot1)
603
+ await redis_memory.delete_history(user2, "common_session", chatbot2)
604
+
605
+ print("All tests passed! ✓")
606
+
607
+ finally:
608
+ await redis_memory.close()
609
+
610
+
611
+ if __name__ == "__main__":
612
+ import asyncio
613
+ asyncio.run(test_redis_conversation())
@@ -0,0 +1,46 @@
1
+ """
2
+ Models for the Parrot application.
3
+ Includes definitions for various data structures used in the application,
4
+ such as responses, outputs, and configurations.
5
+ """
6
+
7
+ from .basic import OutputFormat, ToolCall, CompletionUsage
8
+ from .responses import (
9
+ AIMessage,
10
+ SourceDocument,
11
+ AIMessageFactory,
12
+ MessageResponse,
13
+ StreamChunk,
14
+ )
15
+ from .outputs import (
16
+ StructuredOutputConfig,
17
+ BoundingBox,
18
+ ObjectDetectionResult,
19
+ ImageGenerationPrompt,
20
+ SpeakerConfig,
21
+ SpeechGenerationPrompt,
22
+ VideoGenerationPrompt
23
+ )
24
+ from .google import GoogleModel, TTSVoice
25
+ from .generation import VideoGenerationPrompt
26
+
27
+ __all__ = (
28
+ "OutputFormat",
29
+ "ToolCall",
30
+ "CompletionUsage",
31
+ "AIMessage",
32
+ "AIMessageFactory",
33
+ "SourceDocument",
34
+ "MessageResponse",
35
+ "StreamChunk",
36
+ "StructuredOutputConfig",
37
+ "BoundingBox",
38
+ "ObjectDetectionResult",
39
+ "ImageGenerationPrompt",
40
+ "SpeakerConfig",
41
+ "SpeechGenerationPrompt",
42
+ "VideoGenerationPrompt",
43
+ "GoogleModel",
44
+ "TTSVoice",
45
+ "VideoGenerationPrompt",
46
+ )