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/core.py ADDED
@@ -0,0 +1,555 @@
1
+ # ai_parrot/memory/agent_core_memory.py
2
+
3
+ import asyncio
4
+ import pickle
5
+ import json
6
+ from typing import List, Dict, Optional, Tuple
7
+ from datetime import datetime, timedelta
8
+ from dataclasses import dataclass
9
+ from uuid import uuid4
10
+ from pathlib import Path
11
+ import redis.asyncio as redis
12
+ import numpy as np
13
+ from sentence_transformers import SentenceTransformer
14
+ from bm25s import BM25
15
+ from transformers import pipeline
16
+ from apscheduler.schedulers.asyncio import AsyncIOScheduler
17
+ from navconfig import BASE_DIR
18
+
19
+
20
+ @dataclass
21
+ class MemoryEntry:
22
+ """Memory Entry structure."""
23
+ id: str
24
+ query: str
25
+ response_summary: str
26
+ tools_used: List[str]
27
+ outcome: str
28
+ embedding: np.ndarray
29
+ metadata: Dict
30
+ timestamp: datetime
31
+ access_count: int = 0
32
+ source: str = "memory" # for ranking
33
+ score: float = 0.0
34
+ final_score: float = 0.0
35
+
36
+
37
+ class ValueScorer:
38
+ """Heurísticas para determinar valor de una interacción"""
39
+
40
+ @staticmethod
41
+ def score_interaction(
42
+ query: str,
43
+ response: str,
44
+ tools_used: List[str],
45
+ outcome: str,
46
+ user_feedback: Optional[str] = None
47
+ ) -> float:
48
+ score = 0.0
49
+
50
+ # Outcome positivo
51
+ if outcome in {'resolved', 'successful'}:
52
+ score += 0.3
53
+
54
+ # Usó herramientas (no fue solo conversacional)
55
+ if tools_used:
56
+ score += 0.2
57
+
58
+ # Query substantiva
59
+ if len(query.split()) > 5:
60
+ score += 0.1
61
+
62
+ # Respuesta substantiva
63
+ if len(response) > 100:
64
+ score += 0.2
65
+
66
+ # Feedback explícito del usuario
67
+ if user_feedback == 'positive':
68
+ score += 0.3
69
+ elif user_feedback == 'negative':
70
+ score -= 0.2
71
+
72
+ return max(0.0, min(score, 1.0))
73
+
74
+ @staticmethod
75
+ def is_valuable(score: float) -> bool:
76
+ return score >= 0.4 # threshold ajustable
77
+
78
+
79
+ class AgentCoreMemory:
80
+ """
81
+ Memoria compartida híbrida: Redis (hot) + PgVector (cold)
82
+ """
83
+
84
+ def __init__(
85
+ self,
86
+ redis_client: redis.Redis,
87
+ pgvector_store, # tu implementación actual de PgVector
88
+ embedder_model: str = "all-MiniLM-L6-v2",
89
+ summarizer_model: str = "facebook/bart-large-cnn",
90
+ redis_ttl_days: int = 7,
91
+ distill_schedule_hours: int = 24
92
+ ):
93
+ self.redis = redis_client
94
+ self.pgvector = pgvector_store
95
+
96
+ # Embeddings
97
+ self.embedder = SentenceTransformer(embedder_model)
98
+
99
+ # Summarization
100
+ self.summarizer = pipeline(
101
+ "summarization",
102
+ model=summarizer_model,
103
+ device=0 if self._has_gpu() else -1
104
+ )
105
+
106
+ # BM25 search (lo construimos lazy)
107
+ self._bm25_index: Dict[str, BM25] = {} # {agent_type: BM25}
108
+ self.bm25_cache_dir = Path(BASE_DIR) / "cache" / "bm25"
109
+ self.bm25_cache_dir.mkdir(parents=True, exist_ok=True)
110
+
111
+ # Config
112
+ self.redis_ttl = redis_ttl_days * 24 * 3600
113
+
114
+ # Agent registry
115
+ self.agent_registry: Dict[str, Dict] = {}
116
+
117
+ # Scheduler para destilación periódica
118
+ self.scheduler = AsyncIOScheduler()
119
+ self.scheduler.add_job(
120
+ self._distillation_job,
121
+ 'interval',
122
+ hours=distill_schedule_hours,
123
+ id='memory_distillation'
124
+ )
125
+ # load bm25 indexes on startup:
126
+ self._load_bm25_indices()
127
+
128
+ def _get_bm25_cache_path(self, agent_type: str) -> Path:
129
+ return self.bm25_cache_dir / f"{agent_type}_bm25.pkl"
130
+
131
+ def _load_bm25_indices(self):
132
+ """Carga índices BM25 desde disco al inicio"""
133
+ for agent_type in self.agent_registry.keys():
134
+ cache_path = self._get_bm25_cache_path(agent_type)
135
+ if cache_path.exists():
136
+ try:
137
+ with open(cache_path, 'rb') as f:
138
+ self._bm25_index[agent_type] = pickle.load(f)
139
+ print(f"[BM25] Loaded index for {agent_type}")
140
+ except Exception as e:
141
+ print(f"[BM25] Failed to load index for {agent_type}: {e}")
142
+
143
+ async def _save_bm25_index(self, agent_type: str):
144
+ """Persiste índice BM25 a disco"""
145
+ if agent_type not in self._bm25_index:
146
+ return
147
+
148
+ cache_path = self._get_bm25_cache_path(agent_type)
149
+ try:
150
+ with open(cache_path, 'wb') as f:
151
+ pickle.dump(self._bm25_index[agent_type], f)
152
+ print(f"[BM25] Saved index for {agent_type}")
153
+ except Exception as e:
154
+ print(f"[BM25] Failed to save index for {agent_type}: {e}")
155
+
156
+ async def _rebuild_and_save_bm25(self, agent_type: str):
157
+ """Reconstruye y persiste BM25 index"""
158
+ # Fetch todas las memorias
159
+ pattern = f"agentcore:{agent_type}:memory:*"
160
+ texts = []
161
+
162
+ async for key in self.redis.scan_iter(match=pattern):
163
+ data = await self.redis.hgetall(key)
164
+ if data and b'query' in data:
165
+ texts.append(data[b'query'].decode())
166
+
167
+ if texts:
168
+ # Rebuild index
169
+ self._bm25_index[agent_type] = BM25()
170
+ self._bm25_index[agent_type].index(texts)
171
+
172
+ # Save to disk
173
+ await self._save_bm25_index(agent_type)
174
+
175
+ def start(self):
176
+ """Inicia el scheduler de destilación"""
177
+ self.scheduler.start()
178
+
179
+ def shutdown(self):
180
+ """Detiene el scheduler"""
181
+ self.scheduler.shutdown()
182
+
183
+ def register_agent_expertise(
184
+ self,
185
+ agent_type: str,
186
+ expertise_domains: List[str],
187
+ expertise_description: str
188
+ ):
189
+ """Registra dominios de experiencia de un agente"""
190
+ self.agent_registry[agent_type] = {
191
+ 'domains': expertise_domains,
192
+ 'description': expertise_description,
193
+ 'embedding': self.embedder.encode(expertise_description)
194
+ }
195
+
196
+ # ==================== WRITE: Store Memory ====================
197
+
198
+ async def store_interaction(
199
+ self,
200
+ agent_type: str,
201
+ query: str,
202
+ response: str,
203
+ tools_used: List[str] = None,
204
+ outcome: str = 'unknown',
205
+ user_feedback: Optional[str] = None,
206
+ metadata: Dict = None
207
+ ) -> Optional[str]:
208
+ """
209
+ Almacena una interacción en Redis si es valiosa
210
+
211
+ Returns:
212
+ memory_id si se almacenó, None si se descartó
213
+ """
214
+ tools_used = tools_used or []
215
+ metadata = metadata or {}
216
+
217
+ # 1. Evaluar valor
218
+ value_score = ValueScorer.score_interaction(
219
+ query=query,
220
+ response=response,
221
+ tools_used=tools_used,
222
+ outcome=outcome,
223
+ user_feedback=user_feedback
224
+ )
225
+
226
+ if not ValueScorer.is_valuable(value_score):
227
+ return None # Descartamos interacción de bajo valor
228
+
229
+ # 2. Generar embedding
230
+ embedding = self.embedder.encode(query)
231
+
232
+ # 3. Crear memory entry
233
+ memory_id = str(uuid4())
234
+ key = f"agentcore:{agent_type}:memory:{memory_id}"
235
+
236
+ # 4. Summarizar respuesta si es muy larga
237
+ response_summary = response
238
+ if len(response) > 500:
239
+ response_summary = await self._summarize_text(response, max_length=150)
240
+
241
+ memory_data = {
242
+ "id": memory_id,
243
+ "query": query,
244
+ "response_summary": response_summary,
245
+ "tools_used": json.dumps(tools_used),
246
+ "outcome": outcome,
247
+ "embedding": pickle.dumps(embedding),
248
+ "metadata": json.dumps(metadata),
249
+ "timestamp": datetime.utcnow().isoformat(),
250
+ "access_count": 0,
251
+ "value_score": value_score
252
+ }
253
+
254
+ # 5. Store en Redis con TTL
255
+ await self.redis.hset(key, mapping=memory_data)
256
+ await self.redis.expire(key, self.redis_ttl)
257
+
258
+ # Invalidar BM25 index para reconstruir
259
+ if agent_type in self._bm25_index:
260
+ del self._bm25_index[agent_type]
261
+
262
+ # Trigger rebuild y save en background
263
+ asyncio.create_task(
264
+ self._rebuild_and_save_bm25(agent_type)
265
+ )
266
+
267
+ return memory_id
268
+
269
+ # ==================== READ: Retrieve Memory ====================
270
+
271
+ async def retrieve_memory(
272
+ self,
273
+ query: str,
274
+ agent_type: str,
275
+ top_k: int = 5,
276
+ include_cross_domain: bool = True
277
+ ) -> List[MemoryEntry]:
278
+ """
279
+ Recupera memorias relevantes usando búsqueda híbrida
280
+ BM25 (lexical) + Semantic (embeddings)
281
+ """
282
+ # 1. Determinar agent types relevantes
283
+ agent_types = [agent_type]
284
+ if include_cross_domain:
285
+ cross_agents = await self._route_cross_domain(query, agent_type)
286
+ agent_types.extend(cross_agents)
287
+
288
+ # 2. Búsqueda híbrida por cada agent type
289
+ all_memories = []
290
+ for atype in agent_types:
291
+ memories = await self._hybrid_search(
292
+ query=query,
293
+ agent_type=atype,
294
+ top_k=20 # Recuperamos más para re-ranking
295
+ )
296
+
297
+ # Aplicar decay si es cross-domain
298
+ decay_factor = 1.0 if atype == agent_type else 0.6
299
+ for mem in memories:
300
+ mem.source = "memory_same" if atype == agent_type else "memory_cross"
301
+ mem.score *= decay_factor
302
+
303
+ all_memories.extend(memories)
304
+
305
+ # 3. Re-ranking final
306
+ ranked_memories = self._rerank_memories(all_memories)
307
+
308
+ # 4. Actualizar access_count en Redis (para LRU)
309
+ await self._update_access_counts(ranked_memories[:top_k])
310
+
311
+ return ranked_memories[:top_k]
312
+
313
+ async def _hybrid_search(
314
+ self,
315
+ query: str,
316
+ agent_type: str,
317
+ top_k: int = 20
318
+ ) -> List[MemoryEntry]:
319
+ """Búsqueda híbrida: BM25 + Semantic"""
320
+
321
+ # Fetch todas las memorias de este agent_type
322
+ pattern = f"agentcore:{agent_type}:memory:*"
323
+ keys = []
324
+ async for key in self.redis.scan_iter(match=pattern):
325
+ keys.append(key)
326
+
327
+ if not keys:
328
+ return []
329
+
330
+ # Fetch datos en batch
331
+ pipeline = self.redis.pipeline()
332
+ for key in keys:
333
+ pipeline.hgetall(key)
334
+ results = await pipeline.execute()
335
+
336
+ memories = []
337
+ texts = []
338
+ embeddings = []
339
+
340
+ for key, data in zip(keys, results):
341
+ if not data:
342
+ continue
343
+
344
+ mem = MemoryEntry(
345
+ id=data[b'id'].decode(),
346
+ query=data[b'query'].decode(),
347
+ response_summary=data[b'response_summary'].decode(),
348
+ tools_used=json.loads(data[b'tools_used']),
349
+ outcome=data[b'outcome'].decode(),
350
+ embedding=pickle.loads(data[b'embedding']),
351
+ metadata=json.loads(data[b'metadata']),
352
+ timestamp=datetime.fromisoformat(data[b'timestamp'].decode()),
353
+ access_count=int(data[b'access_count'])
354
+ )
355
+
356
+ memories.append(mem)
357
+ texts.append(mem.query)
358
+ embeddings.append(mem.embedding)
359
+
360
+ if not memories:
361
+ return []
362
+
363
+ # Stage 1: BM25 scoring
364
+ bm25_scores = await self._bm25_score(query, texts, agent_type)
365
+
366
+ # Stage 2: Semantic scoring
367
+ query_embedding = self.embedder.encode(query)
368
+ semantic_scores = [
369
+ self._cosine_similarity(query_embedding, emb)
370
+ for emb in embeddings
371
+ ]
372
+
373
+ # Combine scores (40% BM25, 60% Semantic)
374
+ for i, mem in enumerate(memories):
375
+ mem.score = 0.4 * bm25_scores[i] + 0.6 * semantic_scores[i]
376
+
377
+ # Sort y return top_k
378
+ memories.sort(key=lambda x: x.score, reverse=True)
379
+ return memories[:top_k]
380
+
381
+ async def _bm25_score(
382
+ self,
383
+ query: str,
384
+ corpus_texts: List[str],
385
+ agent_type: str
386
+ ) -> List[float]:
387
+ """BM25 scoring usando bm25s"""
388
+
389
+ # Lazy build index si no existe
390
+ if agent_type not in self._bm25_index:
391
+ self._bm25_index[agent_type] = BM25()
392
+ self._bm25_index[agent_type].index(corpus_texts)
393
+
394
+ scores = self._bm25_index[agent_type].score(query)
395
+
396
+ # Normalizar scores a [0, 1]
397
+ if len(scores) > 0:
398
+ max_score = max(scores)
399
+ if max_score > 0:
400
+ scores = [s / max_score for s in scores]
401
+
402
+ return scores
403
+
404
+ def _rerank_memories(self, memories: List[MemoryEntry]) -> List[MemoryEntry]:
405
+ """
406
+ Re-ranking con source priority:
407
+ - memory_same: score * 0.8
408
+ - memory_cross: score * 0.6
409
+ """
410
+ for mem in memories:
411
+ if mem.source == "memory_same":
412
+ mem.final_score = mem.score * 0.8
413
+ else: # memory_cross
414
+ mem.final_score = mem.score * 0.6
415
+
416
+ return sorted(memories, key=lambda x: x.final_score, reverse=True)
417
+
418
+ async def _update_access_counts(self, memories: List[MemoryEntry]):
419
+ """Incrementa access_count para LRU (más accesados sobreviven)"""
420
+ pipeline = self.redis.pipeline()
421
+ for mem in memories:
422
+ key = f"agentcore:{mem.source.split('_')[1] if '_' in mem.source else 'unknown'}:memory:{mem.id}"
423
+ # Buscar el agent_type correcto - esto es un hack, deberíamos almacenarlo
424
+ # Por ahora, buscamos en todos los agent_types registrados
425
+ for agent_type in self.agent_registry.keys():
426
+ potential_key = f"agentcore:{agent_type}:memory:{mem.id}"
427
+ pipeline.hincrby(potential_key, "access_count", 1)
428
+
429
+ await pipeline.execute()
430
+
431
+ # ==================== ROUTING ====================
432
+
433
+ async def _route_cross_domain(
434
+ self,
435
+ query: str,
436
+ current_agent_type: str,
437
+ threshold: float = 0.5
438
+ ) -> List[str]:
439
+ """Routing semántico a otros agent types"""
440
+ query_emb = self.embedder.encode(query)
441
+
442
+ relevant_agents = []
443
+ for agent_type, meta in self.agent_registry.items():
444
+ if agent_type == current_agent_type:
445
+ continue
446
+
447
+ similarity = self._cosine_similarity(query_emb, meta['embedding'])
448
+
449
+ if similarity > threshold:
450
+ relevant_agents.append((agent_type, similarity))
451
+
452
+ # Return top 2
453
+ relevant_agents.sort(key=lambda x: x[1], reverse=True)
454
+ return [agent for agent, _ in relevant_agents[:2]]
455
+
456
+ # ==================== DISTILLATION ====================
457
+
458
+ async def _distillation_job(self):
459
+ """
460
+ Job periódico: mueve memorias antiguas de Redis a PgVector
461
+ """
462
+ print(f"[Distillation] Starting job at {datetime.utcnow()}")
463
+
464
+ for agent_type in self.agent_registry.keys():
465
+ await self._distill_agent_memories(agent_type)
466
+
467
+ print(f"[Distillation] Job completed")
468
+
469
+ async def _distill_agent_memories(self, agent_type: str):
470
+ """
471
+ Identifica memorias antiguas (idle time alto) y las mueve a PgVector
472
+ """
473
+ pattern = f"agentcore:{agent_type}:memory:*"
474
+
475
+ # 1. Scan todas las keys
476
+ old_memories = []
477
+ async for key in self.redis.scan_iter(match=pattern):
478
+ # Check idle time (tiempo sin acceso)
479
+ idle_time = await self.redis.object('IDLETIME', key)
480
+
481
+ # Si lleva más de 5 días sin acceso, candidato a destilación
482
+ if idle_time and idle_time > (5 * 24 * 3600):
483
+ data = await self.redis.hgetall(key)
484
+ if data:
485
+ old_memories.append((key, data))
486
+
487
+ if not old_memories:
488
+ return
489
+
490
+ print(f"[Distillation] Found {len(old_memories)} old memories for {agent_type}")
491
+
492
+ # 2. Cluster por similitud (opcional, para agrupar antes de sumarizar)
493
+ # Por simplicidad, lo omitimos en v1 y sumariamos individualmente
494
+
495
+ # 3. Summarize y store en PgVector
496
+ for key, data in old_memories:
497
+ try:
498
+ query = data[b'query'].decode()
499
+ response = data[b'response_summary'].decode()
500
+
501
+ # Crear un knowledge summary
502
+ combined_text = f"Query: {query}\nResponse: {response}"
503
+ summary = await self._summarize_text(combined_text, max_length=200)
504
+
505
+ # Store en PgVector
506
+ await self.pgvector.store(
507
+ collection=f"agent_longterm_{agent_type}",
508
+ content=summary,
509
+ embedding=self.embedder.encode(summary),
510
+ metadata={
511
+ 'agent_type': agent_type,
512
+ 'source': 'distilled_memory',
513
+ 'original_query': query,
514
+ 'timestamp': datetime.utcnow().isoformat()
515
+ }
516
+ )
517
+
518
+ # Delete from Redis
519
+ await self.redis.delete(key)
520
+
521
+ except Exception as e:
522
+ print(f"[Distillation] Error processing {key}: {e}")
523
+ continue
524
+
525
+ print(f"[Distillation] Distilled {len(old_memories)} memories for {agent_type}")
526
+
527
+ # ==================== UTILS ====================
528
+
529
+ async def _summarize_text(self, text: str, max_length: int = 150) -> str:
530
+ """Sumariza texto usando BART"""
531
+ try:
532
+ result = self.summarizer(
533
+ text,
534
+ max_length=max_length,
535
+ min_length=30,
536
+ do_sample=False
537
+ )
538
+ return result[0]['summary_text']
539
+ except Exception as e:
540
+ print(f"[Summarization] Error: {e}")
541
+ return text[:max_length] # Fallback: truncate
542
+
543
+ @staticmethod
544
+ def _cosine_similarity(a: np.ndarray, b: np.ndarray) -> float:
545
+ """Cosine similarity entre dos vectores"""
546
+ return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
547
+
548
+ @staticmethod
549
+ def _has_gpu() -> bool:
550
+ """Check si hay GPU disponible"""
551
+ try:
552
+ import torch
553
+ return torch.cuda.is_available()
554
+ except ImportError:
555
+ return False
parrot/memory/file.py ADDED
@@ -0,0 +1,153 @@
1
+ from typing import Dict, List, Optional, Any
2
+ import asyncio
3
+ import aiofiles
4
+ from pathlib import Path
5
+ from datamodel.parsers.json import json_encoder, json_decoder # pylint: disable=E0611 # noqa
6
+ from .abstract import ConversationMemory, ConversationHistory, ConversationTurn
7
+
8
+
9
+ class FileConversationMemory(ConversationMemory):
10
+ """File-based implementation of conversation memory."""
11
+
12
+ def __init__(self, base_path: str = "./conversations"):
13
+ self.base_path = Path(base_path)
14
+ self.base_path.mkdir(exist_ok=True)
15
+ self._lock = asyncio.Lock()
16
+
17
+ def _get_file_path(
18
+ self,
19
+ user_id: str,
20
+ session_id: str,
21
+ chatbot_id: Optional[str] = None
22
+ ) -> Path:
23
+ """Get file path for a conversation history."""
24
+ user_dir = self.base_path / user_id
25
+ if chatbot_id:
26
+ user_dir = user_dir / str(chatbot_id)
27
+ user_dir.mkdir(parents=True, exist_ok=True)
28
+ return user_dir / f"{session_id}.json"
29
+
30
+ async def create_history(
31
+ self,
32
+ user_id: str,
33
+ session_id: str,
34
+ metadata: Optional[Dict[str, Any]] = None,
35
+ chatbot_id: Optional[str] = None
36
+ ) -> ConversationHistory:
37
+ """Create a new conversation history."""
38
+ async with self._lock:
39
+ history = ConversationHistory(
40
+ session_id=session_id,
41
+ user_id=user_id,
42
+ chatbot_id=chatbot_id,
43
+ metadata=metadata or {}
44
+ )
45
+
46
+ file_path = self._get_file_path(user_id, session_id, chatbot_id)
47
+ with open(file_path, 'w', encoding='utf-8') as f:
48
+ json_encoder(history.to_dict(), f, indent=2, ensure_ascii=False)
49
+
50
+ return history
51
+
52
+ async def get_history(
53
+ self,
54
+ user_id: str,
55
+ session_id: str,
56
+ chatbot_id: Optional[str] = None
57
+ ) -> Optional[ConversationHistory]:
58
+ """Get a conversation history."""
59
+ async with self._lock:
60
+ file_path = self._get_file_path(user_id, session_id, chatbot_id)
61
+ if not file_path.exists():
62
+ return None
63
+
64
+ try:
65
+ async with aiofiles.open(file_path, 'r', encoding='utf-8') as f:
66
+ data = await json_decoder(f)
67
+ return ConversationHistory.from_dict(data)
68
+ except (TypeError, KeyError, ValueError):
69
+ return None
70
+
71
+ async def update_history(self, history: ConversationHistory) -> None:
72
+ """Update a conversation history."""
73
+ async with self._lock:
74
+ file_path = self._get_file_path(
75
+ history.user_id,
76
+ history.session_id,
77
+ history.chatbot_id
78
+ )
79
+ async with aiofiles.open(file_path, 'w', encoding='utf-8') as f:
80
+ await json_encoder(history.to_dict(), f, indent=2, ensure_ascii=False)
81
+
82
+ async def add_turn(
83
+ self,
84
+ user_id: str,
85
+ session_id: str,
86
+ turn: ConversationTurn,
87
+ chatbot_id: Optional[str] = None
88
+ ) -> None:
89
+ """Add a turn to the conversation."""
90
+ history = await self.get_history(user_id, session_id, chatbot_id)
91
+ if history:
92
+ history.add_turn(turn)
93
+ await self.update_history(history)
94
+
95
+ async def clear_history(
96
+ self,
97
+ user_id: str,
98
+ session_id: str,
99
+ chatbot_id: Optional[str] = None
100
+ ) -> None:
101
+ """Clear a conversation history."""
102
+ history = await self.get_history(user_id, session_id, chatbot_id)
103
+ if history:
104
+ history.clear_turns()
105
+ await self.update_history(history)
106
+
107
+ async def list_sessions(
108
+ self,
109
+ user_id: str,
110
+ chatbot_id: Optional[str] = None
111
+ ) -> List[str]:
112
+ """List all session IDs for a user."""
113
+ async with self._lock:
114
+ base_user_dir = self.base_path / user_id
115
+ if not base_user_dir.exists():
116
+ return []
117
+
118
+ sessions: List[str] = []
119
+ seen = set()
120
+ if chatbot_id is None:
121
+ for file_path in base_user_dir.glob("*.json"):
122
+ if file_path.stem not in seen:
123
+ seen.add(file_path.stem)
124
+ sessions.append(file_path.stem)
125
+ for subdir in base_user_dir.iterdir():
126
+ if subdir.is_dir():
127
+ for file_path in subdir.glob("*.json"):
128
+ if file_path.stem not in seen:
129
+ seen.add(file_path.stem)
130
+ sessions.append(file_path.stem)
131
+ else:
132
+ chatbot_dir = base_user_dir / str(chatbot_id)
133
+ if chatbot_dir.exists():
134
+ for file_path in chatbot_dir.glob("*.json"):
135
+ if file_path.stem not in seen:
136
+ seen.add(file_path.stem)
137
+ sessions.append(file_path.stem)
138
+
139
+ return sessions
140
+
141
+ async def delete_history(
142
+ self,
143
+ user_id: str,
144
+ session_id: str,
145
+ chatbot_id: Optional[str] = None
146
+ ) -> bool:
147
+ """Delete a conversation history entirely."""
148
+ async with self._lock:
149
+ file_path = self._get_file_path(user_id, session_id, chatbot_id)
150
+ if file_path.exists():
151
+ file_path.unlink()
152
+ return True
153
+ return False