ai-parrot 0.17.2__cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (535) hide show
  1. agentui/.prettierrc +15 -0
  2. agentui/QUICKSTART.md +272 -0
  3. agentui/README.md +59 -0
  4. agentui/env.example +16 -0
  5. agentui/jsconfig.json +14 -0
  6. agentui/package-lock.json +4242 -0
  7. agentui/package.json +34 -0
  8. agentui/scripts/postinstall/apply-patches.mjs +260 -0
  9. agentui/src/app.css +61 -0
  10. agentui/src/app.d.ts +13 -0
  11. agentui/src/app.html +12 -0
  12. agentui/src/components/LoadingSpinner.svelte +64 -0
  13. agentui/src/components/ThemeSwitcher.svelte +159 -0
  14. agentui/src/components/index.js +4 -0
  15. agentui/src/lib/api/bots.ts +60 -0
  16. agentui/src/lib/api/chat.ts +22 -0
  17. agentui/src/lib/api/http.ts +25 -0
  18. agentui/src/lib/components/BotCard.svelte +33 -0
  19. agentui/src/lib/components/ChatBubble.svelte +63 -0
  20. agentui/src/lib/components/Toast.svelte +21 -0
  21. agentui/src/lib/config.ts +20 -0
  22. agentui/src/lib/stores/auth.svelte.ts +73 -0
  23. agentui/src/lib/stores/theme.svelte.js +64 -0
  24. agentui/src/lib/stores/toast.svelte.ts +31 -0
  25. agentui/src/lib/utils/conversation.ts +39 -0
  26. agentui/src/routes/+layout.svelte +20 -0
  27. agentui/src/routes/+page.svelte +232 -0
  28. agentui/src/routes/login/+page.svelte +200 -0
  29. agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
  30. agentui/src/routes/talk/[agentId]/+page.ts +7 -0
  31. agentui/static/README.md +1 -0
  32. agentui/svelte.config.js +11 -0
  33. agentui/tailwind.config.ts +53 -0
  34. agentui/tsconfig.json +3 -0
  35. agentui/vite.config.ts +10 -0
  36. ai_parrot-0.17.2.dist-info/METADATA +472 -0
  37. ai_parrot-0.17.2.dist-info/RECORD +535 -0
  38. ai_parrot-0.17.2.dist-info/WHEEL +6 -0
  39. ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
  40. ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
  41. ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
  42. crew-builder/.prettierrc +15 -0
  43. crew-builder/QUICKSTART.md +259 -0
  44. crew-builder/README.md +113 -0
  45. crew-builder/env.example +17 -0
  46. crew-builder/jsconfig.json +14 -0
  47. crew-builder/package-lock.json +4182 -0
  48. crew-builder/package.json +37 -0
  49. crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
  50. crew-builder/src/app.css +62 -0
  51. crew-builder/src/app.d.ts +13 -0
  52. crew-builder/src/app.html +12 -0
  53. crew-builder/src/components/LoadingSpinner.svelte +64 -0
  54. crew-builder/src/components/ThemeSwitcher.svelte +149 -0
  55. crew-builder/src/components/index.js +9 -0
  56. crew-builder/src/lib/api/bots.ts +60 -0
  57. crew-builder/src/lib/api/chat.ts +80 -0
  58. crew-builder/src/lib/api/client.ts +56 -0
  59. crew-builder/src/lib/api/crew/crew.ts +136 -0
  60. crew-builder/src/lib/api/index.ts +5 -0
  61. crew-builder/src/lib/api/o365/auth.ts +65 -0
  62. crew-builder/src/lib/auth/auth.ts +54 -0
  63. crew-builder/src/lib/components/AgentNode.svelte +43 -0
  64. crew-builder/src/lib/components/BotCard.svelte +33 -0
  65. crew-builder/src/lib/components/ChatBubble.svelte +67 -0
  66. crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
  67. crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
  68. crew-builder/src/lib/components/JsonViewer.svelte +24 -0
  69. crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
  70. crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
  71. crew-builder/src/lib/components/Toast.svelte +67 -0
  72. crew-builder/src/lib/components/Toolbar.svelte +157 -0
  73. crew-builder/src/lib/components/index.ts +10 -0
  74. crew-builder/src/lib/config.ts +8 -0
  75. crew-builder/src/lib/stores/auth.svelte.ts +228 -0
  76. crew-builder/src/lib/stores/crewStore.ts +369 -0
  77. crew-builder/src/lib/stores/theme.svelte.js +145 -0
  78. crew-builder/src/lib/stores/toast.svelte.ts +69 -0
  79. crew-builder/src/lib/utils/conversation.ts +39 -0
  80. crew-builder/src/lib/utils/markdown.ts +122 -0
  81. crew-builder/src/lib/utils/talkHistory.ts +47 -0
  82. crew-builder/src/routes/+layout.svelte +20 -0
  83. crew-builder/src/routes/+page.svelte +539 -0
  84. crew-builder/src/routes/agents/+page.svelte +247 -0
  85. crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
  86. crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
  87. crew-builder/src/routes/builder/+page.svelte +204 -0
  88. crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
  89. crew-builder/src/routes/crew/ask/+page.ts +1 -0
  90. crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
  91. crew-builder/src/routes/login/+page.svelte +197 -0
  92. crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
  93. crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
  94. crew-builder/static/README.md +1 -0
  95. crew-builder/svelte.config.js +11 -0
  96. crew-builder/tailwind.config.ts +53 -0
  97. crew-builder/tsconfig.json +3 -0
  98. crew-builder/vite.config.ts +10 -0
  99. mcp_servers/calculator_server.py +309 -0
  100. parrot/__init__.py +27 -0
  101. parrot/__pycache__/__init__.cpython-310.pyc +0 -0
  102. parrot/__pycache__/version.cpython-310.pyc +0 -0
  103. parrot/_version.py +34 -0
  104. parrot/a2a/__init__.py +48 -0
  105. parrot/a2a/client.py +658 -0
  106. parrot/a2a/discovery.py +89 -0
  107. parrot/a2a/mixin.py +257 -0
  108. parrot/a2a/models.py +376 -0
  109. parrot/a2a/server.py +770 -0
  110. parrot/agents/__init__.py +29 -0
  111. parrot/bots/__init__.py +12 -0
  112. parrot/bots/a2a_agent.py +19 -0
  113. parrot/bots/abstract.py +3139 -0
  114. parrot/bots/agent.py +1129 -0
  115. parrot/bots/basic.py +9 -0
  116. parrot/bots/chatbot.py +669 -0
  117. parrot/bots/data.py +1618 -0
  118. parrot/bots/database/__init__.py +5 -0
  119. parrot/bots/database/abstract.py +3071 -0
  120. parrot/bots/database/cache.py +286 -0
  121. parrot/bots/database/models.py +468 -0
  122. parrot/bots/database/prompts.py +154 -0
  123. parrot/bots/database/retries.py +98 -0
  124. parrot/bots/database/router.py +269 -0
  125. parrot/bots/database/sql.py +41 -0
  126. parrot/bots/db/__init__.py +6 -0
  127. parrot/bots/db/abstract.py +556 -0
  128. parrot/bots/db/bigquery.py +602 -0
  129. parrot/bots/db/cache.py +85 -0
  130. parrot/bots/db/documentdb.py +668 -0
  131. parrot/bots/db/elastic.py +1014 -0
  132. parrot/bots/db/influx.py +898 -0
  133. parrot/bots/db/mock.py +96 -0
  134. parrot/bots/db/multi.py +783 -0
  135. parrot/bots/db/prompts.py +185 -0
  136. parrot/bots/db/sql.py +1255 -0
  137. parrot/bots/db/tools.py +212 -0
  138. parrot/bots/document.py +680 -0
  139. parrot/bots/hrbot.py +15 -0
  140. parrot/bots/kb.py +170 -0
  141. parrot/bots/mcp.py +36 -0
  142. parrot/bots/orchestration/README.md +463 -0
  143. parrot/bots/orchestration/__init__.py +1 -0
  144. parrot/bots/orchestration/agent.py +155 -0
  145. parrot/bots/orchestration/crew.py +3330 -0
  146. parrot/bots/orchestration/fsm.py +1179 -0
  147. parrot/bots/orchestration/hr.py +434 -0
  148. parrot/bots/orchestration/storage/__init__.py +4 -0
  149. parrot/bots/orchestration/storage/memory.py +100 -0
  150. parrot/bots/orchestration/storage/mixin.py +119 -0
  151. parrot/bots/orchestration/verify.py +202 -0
  152. parrot/bots/product.py +204 -0
  153. parrot/bots/prompts/__init__.py +96 -0
  154. parrot/bots/prompts/agents.py +155 -0
  155. parrot/bots/prompts/data.py +216 -0
  156. parrot/bots/prompts/output_generation.py +8 -0
  157. parrot/bots/scraper/__init__.py +3 -0
  158. parrot/bots/scraper/models.py +122 -0
  159. parrot/bots/scraper/scraper.py +1173 -0
  160. parrot/bots/scraper/templates.py +115 -0
  161. parrot/bots/stores/__init__.py +5 -0
  162. parrot/bots/stores/local.py +172 -0
  163. parrot/bots/webdev.py +81 -0
  164. parrot/cli.py +17 -0
  165. parrot/clients/__init__.py +16 -0
  166. parrot/clients/base.py +1491 -0
  167. parrot/clients/claude.py +1191 -0
  168. parrot/clients/factory.py +129 -0
  169. parrot/clients/google.py +4567 -0
  170. parrot/clients/gpt.py +1975 -0
  171. parrot/clients/grok.py +432 -0
  172. parrot/clients/groq.py +986 -0
  173. parrot/clients/hf.py +582 -0
  174. parrot/clients/models.py +18 -0
  175. parrot/conf.py +395 -0
  176. parrot/embeddings/__init__.py +9 -0
  177. parrot/embeddings/base.py +157 -0
  178. parrot/embeddings/google.py +98 -0
  179. parrot/embeddings/huggingface.py +74 -0
  180. parrot/embeddings/openai.py +84 -0
  181. parrot/embeddings/processor.py +88 -0
  182. parrot/exceptions.c +13868 -0
  183. parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
  184. parrot/exceptions.pxd +22 -0
  185. parrot/exceptions.pxi +15 -0
  186. parrot/exceptions.pyx +44 -0
  187. parrot/generators/__init__.py +29 -0
  188. parrot/generators/base.py +200 -0
  189. parrot/generators/html.py +293 -0
  190. parrot/generators/react.py +205 -0
  191. parrot/generators/streamlit.py +203 -0
  192. parrot/generators/template.py +105 -0
  193. parrot/handlers/__init__.py +4 -0
  194. parrot/handlers/agent.py +861 -0
  195. parrot/handlers/agents/__init__.py +1 -0
  196. parrot/handlers/agents/abstract.py +900 -0
  197. parrot/handlers/bots.py +338 -0
  198. parrot/handlers/chat.py +915 -0
  199. parrot/handlers/creation.sql +192 -0
  200. parrot/handlers/crew/ARCHITECTURE.md +362 -0
  201. parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
  202. parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
  203. parrot/handlers/crew/__init__.py +0 -0
  204. parrot/handlers/crew/handler.py +801 -0
  205. parrot/handlers/crew/models.py +229 -0
  206. parrot/handlers/crew/redis_persistence.py +523 -0
  207. parrot/handlers/jobs/__init__.py +10 -0
  208. parrot/handlers/jobs/job.py +384 -0
  209. parrot/handlers/jobs/mixin.py +627 -0
  210. parrot/handlers/jobs/models.py +115 -0
  211. parrot/handlers/jobs/worker.py +31 -0
  212. parrot/handlers/models.py +596 -0
  213. parrot/handlers/o365_auth.py +105 -0
  214. parrot/handlers/stream.py +337 -0
  215. parrot/interfaces/__init__.py +6 -0
  216. parrot/interfaces/aws.py +143 -0
  217. parrot/interfaces/credentials.py +113 -0
  218. parrot/interfaces/database.py +27 -0
  219. parrot/interfaces/google.py +1123 -0
  220. parrot/interfaces/hierarchy.py +1227 -0
  221. parrot/interfaces/http.py +651 -0
  222. parrot/interfaces/images/__init__.py +0 -0
  223. parrot/interfaces/images/plugins/__init__.py +24 -0
  224. parrot/interfaces/images/plugins/abstract.py +58 -0
  225. parrot/interfaces/images/plugins/analisys.py +148 -0
  226. parrot/interfaces/images/plugins/classify.py +150 -0
  227. parrot/interfaces/images/plugins/classifybase.py +182 -0
  228. parrot/interfaces/images/plugins/detect.py +150 -0
  229. parrot/interfaces/images/plugins/exif.py +1103 -0
  230. parrot/interfaces/images/plugins/hash.py +52 -0
  231. parrot/interfaces/images/plugins/vision.py +104 -0
  232. parrot/interfaces/images/plugins/yolo.py +66 -0
  233. parrot/interfaces/images/plugins/zerodetect.py +197 -0
  234. parrot/interfaces/o365.py +978 -0
  235. parrot/interfaces/onedrive.py +822 -0
  236. parrot/interfaces/sharepoint.py +1435 -0
  237. parrot/interfaces/soap.py +257 -0
  238. parrot/loaders/__init__.py +8 -0
  239. parrot/loaders/abstract.py +1131 -0
  240. parrot/loaders/audio.py +199 -0
  241. parrot/loaders/basepdf.py +53 -0
  242. parrot/loaders/basevideo.py +1568 -0
  243. parrot/loaders/csv.py +409 -0
  244. parrot/loaders/docx.py +116 -0
  245. parrot/loaders/epubloader.py +316 -0
  246. parrot/loaders/excel.py +199 -0
  247. parrot/loaders/factory.py +55 -0
  248. parrot/loaders/files/__init__.py +0 -0
  249. parrot/loaders/files/abstract.py +39 -0
  250. parrot/loaders/files/html.py +26 -0
  251. parrot/loaders/files/text.py +63 -0
  252. parrot/loaders/html.py +152 -0
  253. parrot/loaders/markdown.py +442 -0
  254. parrot/loaders/pdf.py +373 -0
  255. parrot/loaders/pdfmark.py +320 -0
  256. parrot/loaders/pdftables.py +506 -0
  257. parrot/loaders/ppt.py +476 -0
  258. parrot/loaders/qa.py +63 -0
  259. parrot/loaders/splitters/__init__.py +10 -0
  260. parrot/loaders/splitters/base.py +138 -0
  261. parrot/loaders/splitters/md.py +228 -0
  262. parrot/loaders/splitters/token.py +143 -0
  263. parrot/loaders/txt.py +26 -0
  264. parrot/loaders/video.py +89 -0
  265. parrot/loaders/videolocal.py +218 -0
  266. parrot/loaders/videounderstanding.py +377 -0
  267. parrot/loaders/vimeo.py +167 -0
  268. parrot/loaders/web.py +599 -0
  269. parrot/loaders/youtube.py +504 -0
  270. parrot/manager/__init__.py +5 -0
  271. parrot/manager/manager.py +1030 -0
  272. parrot/mcp/__init__.py +28 -0
  273. parrot/mcp/adapter.py +105 -0
  274. parrot/mcp/cli.py +174 -0
  275. parrot/mcp/client.py +119 -0
  276. parrot/mcp/config.py +75 -0
  277. parrot/mcp/integration.py +842 -0
  278. parrot/mcp/oauth.py +933 -0
  279. parrot/mcp/server.py +225 -0
  280. parrot/mcp/transports/__init__.py +3 -0
  281. parrot/mcp/transports/base.py +279 -0
  282. parrot/mcp/transports/grpc_session.py +163 -0
  283. parrot/mcp/transports/http.py +312 -0
  284. parrot/mcp/transports/mcp.proto +108 -0
  285. parrot/mcp/transports/quic.py +1082 -0
  286. parrot/mcp/transports/sse.py +330 -0
  287. parrot/mcp/transports/stdio.py +309 -0
  288. parrot/mcp/transports/unix.py +395 -0
  289. parrot/mcp/transports/websocket.py +547 -0
  290. parrot/memory/__init__.py +16 -0
  291. parrot/memory/abstract.py +209 -0
  292. parrot/memory/agent.py +32 -0
  293. parrot/memory/cache.py +175 -0
  294. parrot/memory/core.py +555 -0
  295. parrot/memory/file.py +153 -0
  296. parrot/memory/mem.py +131 -0
  297. parrot/memory/redis.py +613 -0
  298. parrot/models/__init__.py +46 -0
  299. parrot/models/basic.py +118 -0
  300. parrot/models/compliance.py +208 -0
  301. parrot/models/crew.py +395 -0
  302. parrot/models/detections.py +654 -0
  303. parrot/models/generation.py +85 -0
  304. parrot/models/google.py +223 -0
  305. parrot/models/groq.py +23 -0
  306. parrot/models/openai.py +30 -0
  307. parrot/models/outputs.py +285 -0
  308. parrot/models/responses.py +938 -0
  309. parrot/notifications/__init__.py +743 -0
  310. parrot/openapi/__init__.py +3 -0
  311. parrot/openapi/components.yaml +641 -0
  312. parrot/openapi/config.py +322 -0
  313. parrot/outputs/__init__.py +32 -0
  314. parrot/outputs/formats/__init__.py +108 -0
  315. parrot/outputs/formats/altair.py +359 -0
  316. parrot/outputs/formats/application.py +122 -0
  317. parrot/outputs/formats/base.py +351 -0
  318. parrot/outputs/formats/bokeh.py +356 -0
  319. parrot/outputs/formats/card.py +424 -0
  320. parrot/outputs/formats/chart.py +436 -0
  321. parrot/outputs/formats/d3.py +255 -0
  322. parrot/outputs/formats/echarts.py +310 -0
  323. parrot/outputs/formats/generators/__init__.py +0 -0
  324. parrot/outputs/formats/generators/abstract.py +61 -0
  325. parrot/outputs/formats/generators/panel.py +145 -0
  326. parrot/outputs/formats/generators/streamlit.py +86 -0
  327. parrot/outputs/formats/generators/terminal.py +63 -0
  328. parrot/outputs/formats/holoviews.py +310 -0
  329. parrot/outputs/formats/html.py +147 -0
  330. parrot/outputs/formats/jinja2.py +46 -0
  331. parrot/outputs/formats/json.py +87 -0
  332. parrot/outputs/formats/map.py +933 -0
  333. parrot/outputs/formats/markdown.py +172 -0
  334. parrot/outputs/formats/matplotlib.py +237 -0
  335. parrot/outputs/formats/mixins/__init__.py +0 -0
  336. parrot/outputs/formats/mixins/emaps.py +855 -0
  337. parrot/outputs/formats/plotly.py +341 -0
  338. parrot/outputs/formats/seaborn.py +310 -0
  339. parrot/outputs/formats/table.py +397 -0
  340. parrot/outputs/formats/template_report.py +138 -0
  341. parrot/outputs/formats/yaml.py +125 -0
  342. parrot/outputs/formatter.py +152 -0
  343. parrot/outputs/templates/__init__.py +95 -0
  344. parrot/pipelines/__init__.py +0 -0
  345. parrot/pipelines/abstract.py +210 -0
  346. parrot/pipelines/detector.py +124 -0
  347. parrot/pipelines/models.py +90 -0
  348. parrot/pipelines/planogram.py +3002 -0
  349. parrot/pipelines/table.sql +97 -0
  350. parrot/plugins/__init__.py +106 -0
  351. parrot/plugins/importer.py +80 -0
  352. parrot/py.typed +0 -0
  353. parrot/registry/__init__.py +18 -0
  354. parrot/registry/registry.py +594 -0
  355. parrot/scheduler/__init__.py +1189 -0
  356. parrot/scheduler/models.py +60 -0
  357. parrot/security/__init__.py +16 -0
  358. parrot/security/prompt_injection.py +268 -0
  359. parrot/security/security_events.sql +25 -0
  360. parrot/services/__init__.py +1 -0
  361. parrot/services/mcp/__init__.py +8 -0
  362. parrot/services/mcp/config.py +13 -0
  363. parrot/services/mcp/server.py +295 -0
  364. parrot/services/o365_remote_auth.py +235 -0
  365. parrot/stores/__init__.py +7 -0
  366. parrot/stores/abstract.py +352 -0
  367. parrot/stores/arango.py +1090 -0
  368. parrot/stores/bigquery.py +1377 -0
  369. parrot/stores/cache.py +106 -0
  370. parrot/stores/empty.py +10 -0
  371. parrot/stores/faiss_store.py +1157 -0
  372. parrot/stores/kb/__init__.py +9 -0
  373. parrot/stores/kb/abstract.py +68 -0
  374. parrot/stores/kb/cache.py +165 -0
  375. parrot/stores/kb/doc.py +325 -0
  376. parrot/stores/kb/hierarchy.py +346 -0
  377. parrot/stores/kb/local.py +457 -0
  378. parrot/stores/kb/prompt.py +28 -0
  379. parrot/stores/kb/redis.py +659 -0
  380. parrot/stores/kb/store.py +115 -0
  381. parrot/stores/kb/user.py +374 -0
  382. parrot/stores/models.py +59 -0
  383. parrot/stores/pgvector.py +3 -0
  384. parrot/stores/postgres.py +2853 -0
  385. parrot/stores/utils/__init__.py +0 -0
  386. parrot/stores/utils/chunking.py +197 -0
  387. parrot/telemetry/__init__.py +3 -0
  388. parrot/telemetry/mixin.py +111 -0
  389. parrot/template/__init__.py +3 -0
  390. parrot/template/engine.py +259 -0
  391. parrot/tools/__init__.py +23 -0
  392. parrot/tools/abstract.py +644 -0
  393. parrot/tools/agent.py +363 -0
  394. parrot/tools/arangodbsearch.py +537 -0
  395. parrot/tools/arxiv_tool.py +188 -0
  396. parrot/tools/calculator/__init__.py +3 -0
  397. parrot/tools/calculator/operations/__init__.py +38 -0
  398. parrot/tools/calculator/operations/calculus.py +80 -0
  399. parrot/tools/calculator/operations/statistics.py +76 -0
  400. parrot/tools/calculator/tool.py +150 -0
  401. parrot/tools/cloudwatch.py +988 -0
  402. parrot/tools/codeinterpreter/__init__.py +127 -0
  403. parrot/tools/codeinterpreter/executor.py +371 -0
  404. parrot/tools/codeinterpreter/internals.py +473 -0
  405. parrot/tools/codeinterpreter/models.py +643 -0
  406. parrot/tools/codeinterpreter/prompts.py +224 -0
  407. parrot/tools/codeinterpreter/tool.py +664 -0
  408. parrot/tools/company_info/__init__.py +6 -0
  409. parrot/tools/company_info/tool.py +1138 -0
  410. parrot/tools/correlationanalysis.py +437 -0
  411. parrot/tools/database/abstract.py +286 -0
  412. parrot/tools/database/bq.py +115 -0
  413. parrot/tools/database/cache.py +284 -0
  414. parrot/tools/database/models.py +95 -0
  415. parrot/tools/database/pg.py +343 -0
  416. parrot/tools/databasequery.py +1159 -0
  417. parrot/tools/db.py +1800 -0
  418. parrot/tools/ddgo.py +370 -0
  419. parrot/tools/decorators.py +271 -0
  420. parrot/tools/dftohtml.py +282 -0
  421. parrot/tools/document.py +549 -0
  422. parrot/tools/ecs.py +819 -0
  423. parrot/tools/edareport.py +368 -0
  424. parrot/tools/elasticsearch.py +1049 -0
  425. parrot/tools/employees.py +462 -0
  426. parrot/tools/epson/__init__.py +96 -0
  427. parrot/tools/excel.py +683 -0
  428. parrot/tools/file/__init__.py +13 -0
  429. parrot/tools/file/abstract.py +76 -0
  430. parrot/tools/file/gcs.py +378 -0
  431. parrot/tools/file/local.py +284 -0
  432. parrot/tools/file/s3.py +511 -0
  433. parrot/tools/file/tmp.py +309 -0
  434. parrot/tools/file/tool.py +501 -0
  435. parrot/tools/file_reader.py +129 -0
  436. parrot/tools/flowtask/__init__.py +19 -0
  437. parrot/tools/flowtask/tool.py +761 -0
  438. parrot/tools/gittoolkit.py +508 -0
  439. parrot/tools/google/__init__.py +18 -0
  440. parrot/tools/google/base.py +169 -0
  441. parrot/tools/google/tools.py +1251 -0
  442. parrot/tools/googlelocation.py +5 -0
  443. parrot/tools/googleroutes.py +5 -0
  444. parrot/tools/googlesearch.py +5 -0
  445. parrot/tools/googlesitesearch.py +5 -0
  446. parrot/tools/googlevoice.py +2 -0
  447. parrot/tools/gvoice.py +695 -0
  448. parrot/tools/ibisworld/README.md +225 -0
  449. parrot/tools/ibisworld/__init__.py +11 -0
  450. parrot/tools/ibisworld/tool.py +366 -0
  451. parrot/tools/jiratoolkit.py +1718 -0
  452. parrot/tools/manager.py +1098 -0
  453. parrot/tools/math.py +152 -0
  454. parrot/tools/metadata.py +476 -0
  455. parrot/tools/msteams.py +1621 -0
  456. parrot/tools/msword.py +635 -0
  457. parrot/tools/multidb.py +580 -0
  458. parrot/tools/multistoresearch.py +369 -0
  459. parrot/tools/networkninja.py +167 -0
  460. parrot/tools/nextstop/__init__.py +4 -0
  461. parrot/tools/nextstop/base.py +286 -0
  462. parrot/tools/nextstop/employee.py +733 -0
  463. parrot/tools/nextstop/store.py +462 -0
  464. parrot/tools/notification.py +435 -0
  465. parrot/tools/o365/__init__.py +42 -0
  466. parrot/tools/o365/base.py +295 -0
  467. parrot/tools/o365/bundle.py +522 -0
  468. parrot/tools/o365/events.py +554 -0
  469. parrot/tools/o365/mail.py +992 -0
  470. parrot/tools/o365/onedrive.py +497 -0
  471. parrot/tools/o365/sharepoint.py +641 -0
  472. parrot/tools/openapi_toolkit.py +904 -0
  473. parrot/tools/openweather.py +527 -0
  474. parrot/tools/pdfprint.py +1001 -0
  475. parrot/tools/powerbi.py +518 -0
  476. parrot/tools/powerpoint.py +1113 -0
  477. parrot/tools/pricestool.py +146 -0
  478. parrot/tools/products/__init__.py +246 -0
  479. parrot/tools/prophet_tool.py +171 -0
  480. parrot/tools/pythonpandas.py +630 -0
  481. parrot/tools/pythonrepl.py +910 -0
  482. parrot/tools/qsource.py +436 -0
  483. parrot/tools/querytoolkit.py +395 -0
  484. parrot/tools/quickeda.py +827 -0
  485. parrot/tools/resttool.py +553 -0
  486. parrot/tools/retail/__init__.py +0 -0
  487. parrot/tools/retail/bby.py +528 -0
  488. parrot/tools/sandboxtool.py +703 -0
  489. parrot/tools/sassie/__init__.py +352 -0
  490. parrot/tools/scraping/__init__.py +7 -0
  491. parrot/tools/scraping/docs/select.md +466 -0
  492. parrot/tools/scraping/documentation.md +1278 -0
  493. parrot/tools/scraping/driver.py +436 -0
  494. parrot/tools/scraping/models.py +576 -0
  495. parrot/tools/scraping/options.py +85 -0
  496. parrot/tools/scraping/orchestrator.py +517 -0
  497. parrot/tools/scraping/readme.md +740 -0
  498. parrot/tools/scraping/tool.py +3115 -0
  499. parrot/tools/seasonaldetection.py +642 -0
  500. parrot/tools/shell_tool/__init__.py +5 -0
  501. parrot/tools/shell_tool/actions.py +408 -0
  502. parrot/tools/shell_tool/engine.py +155 -0
  503. parrot/tools/shell_tool/models.py +322 -0
  504. parrot/tools/shell_tool/tool.py +442 -0
  505. parrot/tools/site_search.py +214 -0
  506. parrot/tools/textfile.py +418 -0
  507. parrot/tools/think.py +378 -0
  508. parrot/tools/toolkit.py +298 -0
  509. parrot/tools/webapp_tool.py +187 -0
  510. parrot/tools/whatif.py +1279 -0
  511. parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
  512. parrot/tools/workday/__init__.py +6 -0
  513. parrot/tools/workday/models.py +1389 -0
  514. parrot/tools/workday/tool.py +1293 -0
  515. parrot/tools/yfinance_tool.py +306 -0
  516. parrot/tools/zipcode.py +217 -0
  517. parrot/utils/__init__.py +2 -0
  518. parrot/utils/helpers.py +73 -0
  519. parrot/utils/parsers/__init__.py +5 -0
  520. parrot/utils/parsers/toml.c +12078 -0
  521. parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
  522. parrot/utils/parsers/toml.pyx +21 -0
  523. parrot/utils/toml.py +11 -0
  524. parrot/utils/types.cpp +20936 -0
  525. parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
  526. parrot/utils/types.pyx +213 -0
  527. parrot/utils/uv.py +11 -0
  528. parrot/version.py +10 -0
  529. parrot/yaml-rs/Cargo.lock +350 -0
  530. parrot/yaml-rs/Cargo.toml +19 -0
  531. parrot/yaml-rs/pyproject.toml +19 -0
  532. parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
  533. parrot/yaml-rs/src/lib.rs +222 -0
  534. requirements/docker-compose.yml +24 -0
  535. requirements/requirements-dev.txt +21 -0
@@ -0,0 +1,915 @@
1
+ from typing import Union
2
+ from collections import defaultdict
3
+ import re
4
+ import uuid
5
+ import asyncio
6
+ import importlib
7
+ import inspect
8
+ from pathlib import Path
9
+ from aiohttp import web
10
+ from asyncdb.exceptions.exceptions import NoDataFound # pylint: disable=E0611 # noqa
11
+ from datamodel.exceptions import ValidationError # pylint: disable=E0611 # noqa
12
+ from datamodel.parsers.json import json_encoder # noqa pylint: disable=E0611
13
+ from navigator_auth.decorators import (
14
+ is_authenticated,
15
+ user_session,
16
+ allowed_organizations
17
+ )
18
+ from navigator.views import BaseView
19
+ from ..bots.abstract import AbstractBot
20
+ from ..loaders.abstract import AbstractLoader
21
+ from ..loaders.factory import AVAILABLE_LOADERS
22
+ from ..loaders.markdown import MarkdownLoader
23
+ from .models import BotModel
24
+ from ..models.responses import AIMessage
25
+ from ..outputs import OutputFormatter, OutputMode
26
+
27
+
28
+
29
+ @is_authenticated()
30
+ @user_session()
31
+ class ChatHandler(BaseView):
32
+ """
33
+ ChatHandler.
34
+ description: Chat Handler for Parrot Application.
35
+ """
36
+
37
+ async def get(self, **kwargs):
38
+ """
39
+ Obtener información de un chatbot
40
+ ---
41
+ tags:
42
+ - chatbots
43
+ summary: Info de un chatbot o bienvenida al servicio
44
+ description: |
45
+ Si no se especifica nombre de chatbot, retorna mensaje de bienvenida.
46
+ Si se especifica nombre, retorna configuración y detalles del chatbot.
47
+ operationId: getChatbotInfo
48
+ parameters:
49
+ - $ref: "#/components/parameters/ChatbotName"
50
+ responses:
51
+ "200":
52
+ description: Información del chatbot o mensaje de bienvenida
53
+ content:
54
+ application/json:
55
+ schema:
56
+ oneOf:
57
+ - type: object
58
+ properties:
59
+ message:
60
+ type: string
61
+ example: "Welcome to Parrot Chatbot Service."
62
+ - $ref: "#/components/schemas/ChatbotInfo"
63
+ examples:
64
+ welcome:
65
+ summary: Sin chatbot especificado
66
+ value:
67
+ message: "Welcome to Parrot Chatbot Service."
68
+ chatbot_info:
69
+ summary: Con chatbot especificado
70
+ value:
71
+ chatbot: "nextstop"
72
+ description: "Travel planning assistant"
73
+ role: "You are a helpful travel agent"
74
+ embedding_model: "text-embedding-3-small"
75
+ llm: "ChatOpenAI(model='gpt-4', temperature=0.7)"
76
+ temperature: 0.7
77
+ config_file: "/config/bots/nextstop.yaml"
78
+ "404":
79
+ $ref: "#/components/responses/NotFound"
80
+ "401":
81
+ $ref: "#/components/responses/Unauthorized"
82
+ security:
83
+ - sessionAuth: []
84
+ """
85
+ if (name := self.request.match_info.get('chatbot_name', None)):
86
+ # retrieve chatbof information:
87
+ manager = self.request.app['bot_manager']
88
+ chatbot = await manager.get_bot(name)
89
+ if not chatbot:
90
+ return self.error(
91
+ f"Chatbot {name} not found.",
92
+ status=404
93
+ )
94
+ config_file = getattr(chatbot, 'config_file', None)
95
+ return self.json_response({
96
+ "chatbot": chatbot.name,
97
+ "description": chatbot.description,
98
+ "role": chatbot.role,
99
+ "embedding_model": chatbot.embedding_model,
100
+ "llm": f"{chatbot.llm!r}",
101
+ "temperature": chatbot.llm.temperature,
102
+ "config_file": config_file
103
+ })
104
+ else:
105
+ return self.json_response({
106
+ "message": "Welcome to Parrot Chatbot Service."
107
+ })
108
+
109
+ def _check_methods(self, bot: AbstractBot, method_name: str):
110
+ """Check if the method exists in the bot and is callable."""
111
+ forbidden_methods = {
112
+ '__init__', '__del__', '__getattribute__', '__setattr__',
113
+ 'configure', '_setup_database_tools', 'save', 'delete',
114
+ 'update', 'insert', '__dict__', '__class__', 'retrieval',
115
+ '_define_prompt', 'configure_llm', 'configure_store', 'default_tools'
116
+ }
117
+ if not method_name:
118
+ return None
119
+ if method_name.startswith('_') or method_name in forbidden_methods:
120
+ raise AttributeError(
121
+ f"Method {method_name} error, not found or forbidden."
122
+ )
123
+ if not hasattr(bot, method_name):
124
+ raise AttributeError(
125
+ f"Method {method_name} error, not found or forbidden."
126
+ )
127
+ method = getattr(bot, method_name)
128
+ if not callable(method):
129
+ raise TypeError(
130
+ f"Attribute {method_name} is not callable in bot {bot.name}."
131
+ )
132
+ return method
133
+
134
+ async def post(self, *args, **kwargs):
135
+ """
136
+ Interactuar con un chatbot
137
+ ---
138
+ tags:
139
+ - chatbots
140
+ summary: Enviar mensaje a un chatbot
141
+ description: |
142
+ Endpoint principal para interactuar con chatbots. Soporta:
143
+
144
+ - Conversaciones con contexto (RAG)
145
+ - Búsqueda en vector store (similarity, MMR, ensemble)
146
+ - Override de LLM y modelo
147
+ - Upload de archivos (multipart/form-data)
148
+ - Invocación de métodos personalizados del bot
149
+
150
+ ## Modos de uso
151
+
152
+ ### 1. Chat básico
153
+ ```json
154
+ {
155
+ "query": "¿Cuál es la capital de Francia?"
156
+ }
157
+ ```
158
+
159
+ ### 2. Chat con configuración personalizada
160
+ ```json
161
+ {
162
+ "query": "Explica el concepto de RAG",
163
+ "llm": "openai",
164
+ "model": "gpt-4-turbo",
165
+ "temperature": 0.3,
166
+ "search_type": "mmr",
167
+ "return_sources": true
168
+ }
169
+ ```
170
+
171
+ ### 3. Invocar método personalizado
172
+ ```
173
+ POST /api/v1/chat/{chatbot_name}/summarize
174
+ {
175
+ "text": "Long text to summarize...",
176
+ "max_length": 100
177
+ }
178
+ ```
179
+
180
+ ### 4. Upload de archivos
181
+ ```
182
+ Content-Type: multipart/form-data
183
+
184
+ query: "Analiza este documento"
185
+ file: [documento.pdf]
186
+ ```
187
+
188
+ operationId: chatWithBot
189
+ parameters:
190
+ - $ref: "#/components/parameters/ChatbotName"
191
+ - $ref: "#/components/parameters/MethodName"
192
+ requestBody:
193
+ required: true
194
+ description: Mensaje y configuración de la conversación
195
+ content:
196
+ application/json:
197
+ schema:
198
+ $ref: "#/components/schemas/ChatRequest"
199
+ examples:
200
+ basic_chat:
201
+ summary: Chat básico
202
+ value:
203
+ query: "¿Cuál es el mejor momento para visitar Japón?"
204
+
205
+ advanced_chat:
206
+ summary: Chat con opciones avanzadas
207
+ value:
208
+ query: "Explícame sobre inteligencia artificial"
209
+ search_type: "mmr"
210
+ return_sources: true
211
+ return_context: false
212
+ llm: "openai"
213
+ model: "gpt-4-turbo"
214
+ temperature: 0.5
215
+ max_tokens: 1000
216
+ session_id: "session_abc123"
217
+
218
+ contextual_chat:
219
+ summary: Chat con contexto de sesión
220
+ value:
221
+ query: "¿Y cuál es el clima típico?"
222
+ session_id: "session_abc123"
223
+ search_type: "similarity"
224
+
225
+ multipart/form-data:
226
+ schema:
227
+ type: object
228
+ required:
229
+ - query
230
+ properties:
231
+ query:
232
+ type: string
233
+ description: Pregunta o mensaje
234
+ file:
235
+ type: string
236
+ format: binary
237
+ description: Archivo a procesar (PDF, TXT, MD, etc.)
238
+ search_type:
239
+ type: string
240
+ enum: [similarity, mmr, ensemble]
241
+ return_sources:
242
+ type: boolean
243
+
244
+ responses:
245
+ "200":
246
+ description: Respuesta exitosa del chatbot
247
+ content:
248
+ application/json:
249
+ schema:
250
+ $ref: "#/components/schemas/ChatResponse"
251
+ examples:
252
+ simple_response:
253
+ summary: Respuesta simple
254
+ value:
255
+ response: "La mejor época para visitar Japón es durante la primavera (marzo-mayo) para ver los cerezos en flor, o en otoño (septiembre-noviembre) por el clima agradable y los colores del follaje."
256
+ session_id: "session_abc123"
257
+
258
+ response_with_sources:
259
+ summary: Respuesta con fuentes
260
+ value:
261
+ response: "La mejor época para visitar Japón es durante la primavera..."
262
+ sources:
263
+ - content: "Japan's cherry blossom season typically occurs in March..."
264
+ metadata:
265
+ source: "japan_travel_guide.pdf"
266
+ page: 12
267
+ title: "Best Times to Visit Japan"
268
+ score: 0.92
269
+ - content: "Autumn in Japan offers spectacular foliage..."
270
+ metadata:
271
+ source: "seasonal_travel.pdf"
272
+ page: 5
273
+ score: 0.87
274
+ session_id: "session_abc123"
275
+ metadata:
276
+ model: "gpt-4-turbo"
277
+ temperature: 0.7
278
+ tokens_used: 245
279
+ response_time: 1.23
280
+
281
+ "400":
282
+ $ref: "#/components/responses/BadRequest"
283
+ "401":
284
+ $ref: "#/components/responses/Unauthorized"
285
+ "404":
286
+ description: Chatbot no encontrado
287
+ content:
288
+ application/json:
289
+ schema:
290
+ $ref: "#/components/schemas/ErrorResponse"
291
+ example:
292
+ error: "Not Found"
293
+ message: "Chatbot 'travel_bot' not found."
294
+ "422":
295
+ $ref: "#/components/responses/ValidationError"
296
+ "500":
297
+ $ref: "#/components/responses/InternalError"
298
+
299
+ security:
300
+ - sessionAuth: []
301
+ """
302
+ app = self.request.app
303
+ name = self.request.match_info.get('chatbot_name', None)
304
+ method_name = self.request.match_info.get('method_name', None)
305
+ qs = self.query_parameters(self.request)
306
+ try:
307
+ attachments, data = await self.handle_upload()
308
+ except web.HTTPUnsupportedMediaType:
309
+ # if no file is provided, then is a JSON request:
310
+ data = await self.request.json()
311
+ attachments = {}
312
+ if 'llm' in qs:
313
+ # passing another LLM to the Chatbot:
314
+ llm = data.pop('llm')
315
+ model = data.pop('model', None)
316
+ else:
317
+ llm = None
318
+ model = None
319
+ try:
320
+ manager = app['bot_manager']
321
+ except KeyError:
322
+ return self.json_response(
323
+ {
324
+ "message": "Chatbot Manager is not installed."
325
+ },
326
+ status=404
327
+ )
328
+ try:
329
+ chatbot: AbstractBot = await manager.get_bot(name)
330
+ if not chatbot:
331
+ raise KeyError(
332
+ f"Chatbot {name} not found."
333
+ )
334
+ except (TypeError, KeyError):
335
+ return self.json_response(
336
+ {
337
+ "message": f"Chatbot {name} not found."
338
+ },
339
+ status=404
340
+ )
341
+ # getting the question:
342
+ question = data.pop('query', None)
343
+ search_type = data.pop('search_type', 'similarity')
344
+ return_sources = data.pop('return_sources', True)
345
+ return_context = data.pop('return_context', False)
346
+ try:
347
+ session = self.request.session
348
+ except AttributeError:
349
+ session = None
350
+ if not session:
351
+ return self.json_response(
352
+ {
353
+ "message": "User Session is required to interact with a Chatbot."
354
+ },
355
+ status=400
356
+ )
357
+ stream = data.pop('stream', False)
358
+ if isinstance(stream, str):
359
+ stream = stream.lower() == 'true'
360
+ try:
361
+ async with chatbot.retrieval(self.request, app=app, llm=llm) as bot:
362
+ session_id = session.get('session_id', None)
363
+ user_id = session.get('user_id', None)
364
+ if not session_id:
365
+ session_id = str(uuid.uuid4())
366
+ if method:= self._check_methods(bot, method_name):
367
+ sig = inspect.signature(method)
368
+ method_params = {}
369
+ missing_required = []
370
+ for param_name, param in sig.parameters.items():
371
+ if param_name == 'self' or param_name in 'kwargs':
372
+ continue
373
+ # Handle different parameter types
374
+ if param.kind == inspect.Parameter.VAR_POSITIONAL:
375
+ # *args - skip, we don't handle positional args via JSON
376
+ continue
377
+ elif param.kind == inspect.Parameter.VAR_KEYWORD:
378
+ # **kwargs - pass all remaining data that wasn't matched
379
+ continue
380
+ # Regular parameters
381
+ if param_name in data:
382
+ method_params[param_name] = data[param_name]
383
+ elif param.default == inspect.Parameter.empty:
384
+ # Required parameter missing
385
+ missing_required.append(param_name)
386
+ if param_name in attachments:
387
+ files = attachments[param_name]
388
+ if hasattr(param.annotation, '__origin__'):
389
+ # If the parameter is a file upload, handle accordingly
390
+ method_params[param_name] = files
391
+ else:
392
+ method_params[param_name] = files[0] if files else None
393
+ if missing_required:
394
+ return self.json_response(
395
+ {
396
+ "message": f"Required parameters missing: {', '.join(missing_required)}",
397
+ "required_params": [p for p in sig.parameters.keys() if p != 'self']
398
+ },
399
+ status=400
400
+ )
401
+ try:
402
+ method_params = {**method_params, **data}
403
+ response = await method(
404
+ **method_params
405
+ )
406
+ if isinstance(response, web.Response):
407
+ return response
408
+ return web.json_response(
409
+ response, dumps=json_encoder
410
+ )
411
+ except Exception as exc:
412
+ self.error(
413
+ f"Error invoking method {method_name} on chatbot {name}: {exc}",
414
+ exception=exc,
415
+ status=400
416
+ )
417
+ if not question:
418
+ return self.json_response(
419
+ {
420
+ "message": "Query parameter is required to interact with the chatbot."
421
+ },
422
+ status=400
423
+ )
424
+ if stream:
425
+ response = web.StreamResponse(
426
+ status=200,
427
+ headers={
428
+ 'Content-Type': 'text/event-stream',
429
+ 'Cache-Control': 'no-cache',
430
+ 'Connection': 'keep-alive'
431
+ }
432
+ )
433
+ await response.prepare(self.request)
434
+
435
+ try:
436
+ async for event in await bot.ask_stream(
437
+ question=question,
438
+ session_id=session_id,
439
+ user_id=user_id,
440
+ search_type=search_type,
441
+ llm=llm,
442
+ model=model,
443
+ return_sources=return_sources,
444
+ return_context=return_context,
445
+ request=self.request,
446
+ **data
447
+ ):
448
+ payload = json_encoder(event)
449
+ message = f"data: {payload}\n\n"
450
+ await response.write(message.encode('utf-8'))
451
+ except Exception as exc:
452
+ error_payload = json_encoder({
453
+ "event": "error",
454
+ "data": str(exc)
455
+ })
456
+ await response.write(f"data: {error_payload}\n\n".encode('utf-8'))
457
+ raise
458
+ finally:
459
+ await response.write_eof()
460
+ return response
461
+
462
+ response = await bot.conversation(
463
+ question=question,
464
+ session_id=session_id,
465
+ user_id=user_id,
466
+ search_type=search_type,
467
+ llm=llm,
468
+ model=model,
469
+ return_sources=return_sources,
470
+ return_context=return_context,
471
+ request=self.request,
472
+ **data
473
+ )
474
+ return web.json_response(
475
+ response,
476
+ dumps=json_encoder
477
+ )
478
+ except ValueError as exc:
479
+ return self.error(
480
+ f"{exc}",
481
+ exception=exc,
482
+ status=400
483
+ )
484
+ except web.HTTPException as exc:
485
+ return self.error(
486
+ f"{exc}",
487
+ exception=exc,
488
+ status=400
489
+ )
490
+ except Exception as exc:
491
+ return self.error(
492
+ f"Error invoking chatbot {name}: {exc}",
493
+ exception=exc,
494
+ status=400
495
+ )
496
+
497
+
498
+ @is_authenticated()
499
+ @user_session()
500
+ class BotHandler(BaseView):
501
+ """BotHandler.
502
+ description: Bot Handler for Parrot Application.
503
+ Use this handler to interact with a brand new chatbot, consuming a configuration.
504
+ """
505
+ async def _create_bot(self, name: str, data: dict):
506
+ """Create a New Bot (passing a configuration).
507
+ """
508
+ db = self.request.app['database']
509
+ async with await db.acquire() as conn:
510
+ BotModel.Meta.connection = conn
511
+ # check first if chatbot already exists:
512
+ exists = None
513
+ try:
514
+ exists = await BotModel.get(name=name)
515
+ except NoDataFound:
516
+ exists = False
517
+ if exists:
518
+ return self.json_response(
519
+ {
520
+ "message": f"Chatbot {name} already exists with id {exists.chatbot_id}"
521
+ },
522
+ status=202
523
+ )
524
+ try:
525
+ chatbot_model = BotModel(
526
+ name=name,
527
+ **data
528
+ )
529
+ chatbot_model = await chatbot_model.insert()
530
+ return chatbot_model
531
+ except ValidationError:
532
+ raise
533
+ except Exception:
534
+ raise
535
+
536
+ async def put(self):
537
+ """Create a New Bot (passing a configuration).
538
+ """
539
+ try:
540
+ manager = self.request.app['bot_manager']
541
+ except KeyError:
542
+ return self.json_response(
543
+ {
544
+ "message": "Chatbot Manager is not installed."
545
+ },
546
+ status=404
547
+ )
548
+ # TODO: Making a Validation of data
549
+ data = await self.request.json()
550
+ name = data.pop('name', None)
551
+ if not name:
552
+ return self.json_response(
553
+ {
554
+ "message": "Name for Bot Creation is required."
555
+ },
556
+ status=400
557
+ )
558
+ try:
559
+ bot = manager.create_bot(name=name, **data)
560
+ except Exception as exc:
561
+ print(exc.__traceback__)
562
+ return self.error(
563
+ response={
564
+ "message": f"Error creating chatbot {name}.",
565
+ "exception": str(exc),
566
+ "stacktrace": str(exc.__traceback__)
567
+ },
568
+ exception=exc,
569
+ status=400
570
+ )
571
+ try:
572
+ # if bot is created:
573
+ await self._create_bot(name=name, data=data)
574
+ except ValidationError as exc:
575
+ return self.error(
576
+ f"Validation Error for {name}: {exc}",
577
+ exception=exc.payload,
578
+ status=400
579
+ )
580
+ except Exception as exc:
581
+ print(exc.__traceback__)
582
+ return self.error(
583
+ response={
584
+ "message": f"Error creating chatbot {name}.",
585
+ "exception": str(exc),
586
+ "stacktrace": str(exc.__traceback__)
587
+ },
588
+ exception=exc,
589
+ status=400
590
+ )
591
+ try:
592
+ # Then Configure the bot:
593
+ await bot.configure(app=self.request.app)
594
+ return self.json_response(
595
+ {
596
+ "message": f"Chatbot {name} created successfully."
597
+ }
598
+ )
599
+ except Exception as exc:
600
+ return self.error(
601
+ f"Error on chatbot configuration: {name}: {exc}",
602
+ exception=exc,
603
+ status=400
604
+ )
605
+
606
+
607
+ @is_authenticated()
608
+ @user_session()
609
+ class BotManagement(BaseView):
610
+ """BotManagement.
611
+ description: Bot Management Handler for Parrot Application.
612
+ Use this handler to list all available chatbots, upload files, and delete chatbots.
613
+ """
614
+ async def get(self):
615
+ """List all available chatbots.
616
+ """
617
+ try:
618
+ manager = self.request.app['bot_manager']
619
+ except KeyError:
620
+ return self.json_response(
621
+ {
622
+ "message": "Chatbot Manager is not installed."
623
+ },
624
+ status=404
625
+ )
626
+ try:
627
+ all_bots = manager.get_bots()
628
+ bots = []
629
+ for bot_name, bot in all_bots.items():
630
+ bots.append({
631
+ "name": bot_name,
632
+ "chatbot_id": bot.chatbot_id,
633
+ "bot_class": str(bot.__class__.__name__),
634
+ "description": bot.description,
635
+ "backstory": bot.backstory,
636
+ "role": bot.role,
637
+ "embedding_model": bot.embedding_model,
638
+ "llm": f"{bot.llm!r}",
639
+ "temperature": bot.llm.temperature,
640
+ "documents": bot.get_vector_store()
641
+ })
642
+ except Exception as exc:
643
+ return self.error(
644
+ response={
645
+ "message": f"Error retrieving chatbots.",
646
+ "exception": str(exc),
647
+ "stacktrace": str(exc.__traceback__)
648
+ },
649
+ exception=exc,
650
+ status=400
651
+ )
652
+ return self.json_response(
653
+ {
654
+ "bots": bots
655
+ }
656
+ )
657
+
658
+ def _get_loader(self, loader_name: Union[str, type]) -> type:
659
+ """Get the loader class by name."""
660
+ if isinstance(loader_name, type) and issubclass(loader_name, AbstractLoader):
661
+ return loader_name
662
+ if not loader_name:
663
+ return None
664
+ try:
665
+ module = importlib.import_module('parrot.loaders', package=None)
666
+ loader_cls = getattr(module, loader_name, None)
667
+ if not loader_cls:
668
+ raise ValueError(f"Loader not found: {loader_name}")
669
+ if isinstance(loader_cls, type) and issubclass(loader_cls, AbstractLoader):
670
+ return loader_cls
671
+ except Exception:
672
+ pass
673
+ # try submodule guess
674
+ base = loader_name[:-6] if loader_name.endswith("Loader") else loader_name
675
+ candidates = [
676
+ f"parrot.loaders.{base.lower()}",
677
+ f"parrot.loaders.{re.sub(r'(?<!^)(?=[A-Z])','_',base).lower()}",
678
+ ]
679
+ for mod_name in candidates:
680
+ try:
681
+ mod = importlib.import_module(mod_name)
682
+ loader_cls = getattr(mod, loader_name, None)
683
+ if isinstance(loader_cls, type) and issubclass(loader_cls, AbstractLoader):
684
+ return loader_cls
685
+ except Exception:
686
+ continue
687
+ return None
688
+
689
+ def _group_attachments_by_loader(self, attachments, default_loader_cls=None):
690
+ """
691
+ Returns dict[LoaderClass, list[Path]]
692
+ If default_loader_cls is provided, all files go to that loader.
693
+ Otherwise, choose per-file from AVAILABLE_LOADERS by extension, fallback to MarkdownLoader.
694
+ """
695
+ by_loader = defaultdict(list)
696
+ files = []
697
+ for _, values in attachments.items():
698
+ for a in values or []:
699
+ p = a.get("file_path")
700
+ if p is None:
701
+ continue
702
+ files.append(Path(p))
703
+
704
+ if default_loader_cls:
705
+ if not issubclass(default_loader_cls, AbstractLoader):
706
+ raise TypeError(
707
+ f"Default loader must subclass AbstractLoader, got {default_loader_cls}"
708
+ )
709
+ if files:
710
+ by_loader[default_loader_cls].extend(files)
711
+ return by_loader
712
+
713
+ # No default → pick by extension
714
+ for p in files:
715
+ ext = p.suffix.lower()
716
+ loader_cls = AVAILABLE_LOADERS.get(ext, MarkdownLoader)
717
+ by_loader[loader_cls].append(p)
718
+
719
+ return by_loader
720
+
721
+ async def put(self):
722
+ """Upload a file to a chatbot.
723
+ """
724
+ try:
725
+ attachments, form_data = await self.handle_upload()
726
+ except web.HTTPUnsupportedMediaType:
727
+ # if no file is provided, then is a JSON request:
728
+ form_data = await self.request.json()
729
+ attachments = {}
730
+ try:
731
+ manager = self.request.app['bot_manager']
732
+ except KeyError:
733
+ return self.json_response(
734
+ {
735
+ "message": "Chatbot Manager is not installed."
736
+ },
737
+ status=404
738
+ )
739
+ params = self.get_arguments(self.request)
740
+ chatbot_name = params.get('bot', None)
741
+ if not chatbot_name:
742
+ return self.json_response(
743
+ {
744
+ "message": "Chatbot name is required."
745
+ },
746
+ status=400
747
+ )
748
+ try:
749
+ manager = self.request.app['bot_manager']
750
+ except KeyError:
751
+ return self.json_response(
752
+ {
753
+ "message": "Chatbot Manager is not installed."
754
+ },
755
+ status=404
756
+ )
757
+ try:
758
+ chatbot: AbstractBot = await manager.get_bot(chatbot_name)
759
+ if not chatbot:
760
+ raise KeyError(
761
+ f"Chatbot {chatbot_name} not found."
762
+ )
763
+ except (TypeError, KeyError):
764
+ return self.json_response(
765
+ {
766
+ "message": f"Chatbot {chatbot_name} not found."
767
+ },
768
+ status=404
769
+ )
770
+ # Check if Store is loaded, if not, return error:
771
+ if not chatbot.get_vector_store():
772
+ return self.json_response(
773
+ {
774
+ "message": f"Chatbot {chatbot_name} has no Vector Store configured."
775
+ },
776
+ status=400
777
+ )
778
+ # Check if chatbot.store is available:
779
+ if chatbot.store is None:
780
+ # Load the store:
781
+ try:
782
+ store = chatbot.get_vector_store()
783
+ # change "name" to "vector_vector_store"
784
+ if 'name' in store:
785
+ store['vector_store'] = store.pop('name')
786
+ chatbot.define_store(
787
+ **store
788
+ )
789
+ chatbot.configure_store()
790
+ except Exception as e:
791
+ return self.json_response(
792
+ {
793
+ "message": f"Failed to configure store for chatbot {chatbot_name}: {e}"
794
+ },
795
+ status=500
796
+ )
797
+ default_loader = form_data.pop('loader', 'MarkdownLoader')
798
+ source_type = form_data.pop('source_type', 'file')
799
+ # Any extra kwargs for loaders (excluding 'loader' key)
800
+ loader_kwargs = {k: v for k, v in (form_data or {}).items() if k != "loader"}
801
+ loader_cls = self._get_loader(default_loader)
802
+ # --- Group all attachments by loader ---
803
+ by_loader = self._group_attachments_by_loader(
804
+ attachments,
805
+ default_loader_cls=loader_cls
806
+ )
807
+ files_list = []
808
+ loaders_used = []
809
+ if not by_loader and not attachments:
810
+ # if no files were uploaded, using the form_data as a source:
811
+ source = form_data.pop('source', None)
812
+ # Any extra kwargs for loaders (excluding control keys)
813
+ loader_kwargs = {k: v for k, v in (form_data or {}).items()
814
+ if k not in {'loader', 'source_type', 'source'}}
815
+ if not source:
816
+ return self.json_response(
817
+ {"message": "No files/URLs were uploaded and no source provided."},
818
+ status=400
819
+ )
820
+ # If loader not resolved, try to infer: YouTube vs Web
821
+ if loader_cls is None:
822
+ if any('youtu' in u for u in source):
823
+ loader_cls = self._get_loader('YoutubeLoader')
824
+ else:
825
+ loader_cls = self._get_loader('WebLoader')
826
+ documents: list = []
827
+ errors: list = []
828
+ if loader_cls is None:
829
+ return self.json_response(
830
+ {"message": "Loader not found or not specified for URL sources."},
831
+ status=400
832
+ )
833
+ try:
834
+ loader = loader_cls(
835
+ source=source,
836
+ source_type=source_type,
837
+ **loader_kwargs
838
+ )
839
+ docs = await loader.load()
840
+ if isinstance(docs, list):
841
+ documents.extend(docs)
842
+ except Exception as exc:
843
+ errors.append(str(exc))
844
+ loaders_used = [loader_cls.__name__]
845
+ files_list = source
846
+ if attachments:
847
+ if not by_loader:
848
+ return self.json_response(
849
+ {"message": "No supported files found."},
850
+ status=400
851
+ )
852
+ tasks = []
853
+ for loader_cls, files in by_loader.items():
854
+ print(
855
+ f"Loading {len(files)} files with {loader_cls.__name__}"
856
+ )
857
+ try:
858
+ # Each loader receives the full list for that type (avoid per-file loops)
859
+ loader = loader_cls(
860
+ source=files,
861
+ source_type=source_type,
862
+ **loader_kwargs
863
+ )
864
+ tasks.append(loader.load())
865
+ except Exception as exc:
866
+ return self.error(
867
+ f"Error initializing {loader_cls} for chatbot {chatbot_name}: {exc}",
868
+ exception=exc,
869
+ status=400
870
+ )
871
+ results = await asyncio.gather(*tasks, return_exceptions=True)
872
+ # Flatten and handle errors without aborting the whole batch
873
+ documents = []
874
+ errors = []
875
+ try:
876
+ for res in results:
877
+ if isinstance(res, Exception):
878
+ errors.append(str(res))
879
+ elif isinstance(res, list):
880
+ documents.extend(res)
881
+ except Exception as exc:
882
+ return self.error(
883
+ f"Error adding documents to chatbot {chatbot_name}: {exc}",
884
+ exception=exc,
885
+ status=400
886
+ )
887
+ files_list = []
888
+ for _, values in attachments.items():
889
+ for a in values or []:
890
+ p = a.get("file_path")
891
+ if p is None:
892
+ continue
893
+ files_list.append(str(p))
894
+ loaders_used = [cls.__name__ for cls in by_loader.keys()]
895
+ # Load documents into the chatbot
896
+ try:
897
+ if documents:
898
+ await chatbot.store.add_documents(documents)
899
+ except Exception as exc:
900
+ return self.error(
901
+ f"Error adding documents to chatbot {chatbot_name}: {exc}",
902
+ exception=exc,
903
+ status=400
904
+ )
905
+ payload = {
906
+ "bot": chatbot_name,
907
+ "files": files_list,
908
+ "loaders": loaders_used,
909
+ "documents": len(documents),
910
+ "errors": errors,
911
+ }
912
+ return self.json_response(
913
+ payload,
914
+ status=207 if errors else 200
915
+ )