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,938 @@
1
+ from typing import Dict, List, Optional, Any, TypedDict, Union
2
+ from pathlib import Path
3
+ from datetime import datetime
4
+ from dataclasses import dataclass
5
+ from pydantic import BaseModel, Field, model_validator
6
+ from .basic import CompletionUsage, ToolCall
7
+ from .outputs import OutputMode
8
+
9
+
10
+ @dataclass
11
+ class SourceDocument:
12
+ """Enhanced source document information similar to Langchain's format."""
13
+ source: str
14
+ filename: str
15
+ file_path: Optional[str] = None
16
+ source_path: Optional[str] = None
17
+ url: Optional[str] = None
18
+ content_type: Optional[str] = None
19
+ category: Optional[str] = None
20
+ source_type: Optional[str] = None
21
+ source_ext: Optional[str] = None
22
+ page_number: Optional[int] = None
23
+ chunk_id: Optional[str] = None
24
+ parent_document_id: Optional[str] = None
25
+ chunk_index: Optional[int] = None
26
+ score: Optional[float] = None
27
+ metadata: Dict[str, Any] = None
28
+
29
+ def to_dict(self) -> Dict[str, Any]:
30
+ """Convert to dictionary format."""
31
+ return {
32
+ "source": self.source,
33
+ "filename": self.filename,
34
+ "file_path": self.file_path,
35
+ "source_path": self.source_path,
36
+ "url": self.url,
37
+ "content_type": self.content_type,
38
+ "category": self.category,
39
+ "source_type": self.source_type,
40
+ "source_ext": self.source_ext,
41
+ "page_number": self.page_number,
42
+ "chunk_id": self.chunk_id,
43
+ "parent_document_id": self.parent_document_id,
44
+ "chunk_index": self.chunk_index,
45
+ "score": self.score,
46
+ "metadata": self.metadata or {}
47
+ }
48
+
49
+
50
+ class StreamChunk(BaseModel):
51
+ """Represents a chunk in a streaming response."""
52
+ content: str
53
+ is_complete: bool = False
54
+ chunk_id: Optional[str] = None
55
+ turn_id: Optional[str] = None
56
+
57
+
58
+ class MessageResponse(TypedDict):
59
+ """Response structure for LLM messages."""
60
+ id: str
61
+ type: str
62
+ role: str
63
+ content: List[Dict[str, Any]]
64
+ model: str
65
+ stop_reason: Optional[str]
66
+ stop_sequence: Optional[str]
67
+ usage: Dict[str, int]
68
+
69
+
70
+ class AIMessage(BaseModel):
71
+ """Unified AI message response that can handle various output types."""
72
+
73
+ # Core response data
74
+ input: str = Field(
75
+ description="The original input/prompt sent to the LLM"
76
+ )
77
+ output: Any = Field(
78
+ description="The response output - can be text, structured data, dataframe, etc."
79
+ )
80
+ response: Optional[str] = Field(
81
+ default=None,
82
+ description="The textual response from the model, if applicable"
83
+ )
84
+ data: Optional[Any] = Field(
85
+ default=None,
86
+ description="Structured data associated with the response, if applicable"
87
+ )
88
+ code: Optional[str] = Field(
89
+ default=None,
90
+ description="The Python code used for analysis OR the Code generated under request (e.g. JSON definition for a Altair/Vega Chart)."
91
+ )
92
+ images: Optional[List[Path]] = Field(
93
+ default_factory=list,
94
+ description="List of image file paths generated by the model"
95
+ )
96
+ media: Optional[List[Path]] = Field(
97
+ default_factory=list,
98
+ description="List of media files generated by the model"
99
+ )
100
+ files: Optional[List[Path]] = Field(
101
+ default_factory=list,
102
+ description="List of file paths associated with the response"
103
+ )
104
+ documents: Optional[List[Any]] = Field(
105
+ default_factory=list,
106
+ description="List of document file paths associated with the response"
107
+ )
108
+ # Metadata
109
+ model: str = Field(
110
+ description="The model used for generation"
111
+ )
112
+ provider: str = Field(
113
+ description="The LLM provider (openai, groq, claude, etc.)"
114
+ )
115
+ # Usage and performance
116
+ usage: CompletionUsage = Field(
117
+ description="Token usage and timing information"
118
+ )
119
+ # Additional response metadata
120
+ stop_reason: Optional[str] = Field(
121
+ default=None, description="Why the generation stopped"
122
+ )
123
+ finish_reason: Optional[str] = Field(
124
+ default=None, description="Finish reason from provider"
125
+ )
126
+ # Tool usage
127
+ tool_calls: List[ToolCall] = Field(
128
+ default_factory=list,
129
+ description="Tools called during generation"
130
+ )
131
+ # Conversation context
132
+ user_id: Optional[Union[str, int]] = Field(
133
+ default=None, description="User ID for conversation tracking"
134
+ )
135
+ session_id: Optional[str] = Field(
136
+ default=None, description="Session ID for conversation tracking"
137
+ )
138
+ # Timestamps
139
+ created_at: datetime = Field(
140
+ default_factory=datetime.now, description="When the response was created"
141
+ )
142
+ response_time: Optional[float] = Field(
143
+ default=None,
144
+ description="Time taken to generate the response (in seconds)"
145
+ )
146
+ # Raw response for debugging
147
+ raw_response: Optional[Dict[str, Any]] = Field(
148
+ default=None, description="Original response from provider"
149
+ )
150
+ # Conversation turn info
151
+ turn_id: Optional[str] = Field(
152
+ default=None,
153
+ description="Unique ID for this conversation turn"
154
+ )
155
+ # Vector store and conversation history tracking
156
+ used_vector_context: bool = Field(
157
+ default=False,
158
+ description="Whether vector store context was used in generating this response"
159
+ )
160
+ used_conversation_history: bool = Field(
161
+ default=False,
162
+ description="Whether conversation history was used in generating this response"
163
+ )
164
+ vector_context_length: int = Field(
165
+ default=0,
166
+ description="Length of vector context used (in characters)"
167
+ )
168
+ conversation_context_length: int = Field(
169
+ default=0,
170
+ description="Length of conversation context used (in characters)"
171
+ )
172
+ search_results_count: int = Field(
173
+ default=0,
174
+ description="Number of search results retrieved from vector store"
175
+ )
176
+ search_type: Optional[str] = Field(
177
+ default=None,
178
+ description="Type of search performed (similarity, mmr, ensemble, etc.)"
179
+ )
180
+ search_score_threshold: Optional[float] = Field(
181
+ default=None,
182
+ description="Score threshold used for vector search"
183
+ )
184
+ context_sources: Optional[List[str]] = Field(
185
+ default_factory=list,
186
+ description="List of source identifiers for context used"
187
+ )
188
+ source_documents: Optional[List[SourceDocument]] = Field(
189
+ default_factory=list,
190
+ description="List of detailed source documents used for context"
191
+ )
192
+ structured_output: Optional[Union[BaseModel, Any]] = Field(
193
+ default=None,
194
+ description="Immutable original structured output (BaseModel, dataclass, DataFrame)"
195
+ )
196
+ is_structured: bool = Field(
197
+ default=False,
198
+ description="Whether the output is structured"
199
+ )
200
+ metadata: Dict[str, Any] = Field(
201
+ default_factory=dict,
202
+ description="Additional metadata associated with the response"
203
+ )
204
+ output_mode: OutputMode = Field(
205
+ default=OutputMode.DEFAULT,
206
+ description="The output mode used for rendering (markdown, html, json, etc.)"
207
+ )
208
+
209
+ class Config:
210
+ """Pydantic configuration for AIMessage."""
211
+ # Allow arbitrary types for output field (pandas DataFrames, etc.)
212
+ arbitrary_types_allowed = True
213
+
214
+ @property
215
+ def content(self) -> Any:
216
+ """
217
+ Get content as a string. This is an alias for to_text property
218
+ that provides a more intuitive API and compatibility with standard
219
+ AI message formats.
220
+
221
+ Returns:
222
+ str: The text representation of the output
223
+ """
224
+ return self.output
225
+
226
+ @content.setter
227
+ def content(self, value: Any) -> None:
228
+ """
229
+ Set content by updating the output field.
230
+
231
+ Args:
232
+ value: The content string to set
233
+ """
234
+ self.output = value
235
+
236
+ @property
237
+ def to_text(self) -> str:
238
+ """Get text representation of output."""
239
+ if self.response:
240
+ return self.response
241
+ if isinstance(self.output, str):
242
+ return self.output
243
+ elif isinstance(self.output, dict) and 'content' in self.output:
244
+ # Handle MessageResponse-style format
245
+ content = self.output['content']
246
+ if isinstance(content, list) and content:
247
+ return content[0].get('text', str(self.output))
248
+ return str(content)
249
+ elif hasattr(self.output, 'to_string'):
250
+ # Handle pandas DataFrames
251
+ return self.output.to_string()
252
+ else:
253
+ # Return the output *as is*
254
+ return str(self.output)
255
+
256
+ @property
257
+ def has_tools(self) -> bool:
258
+ """Check if tools were used."""
259
+ return len(self.tool_calls) > 0
260
+
261
+ def add_tool_call(self, tool_call: ToolCall) -> None:
262
+ """Add a tool call to the response."""
263
+ self.tool_calls.append(tool_call) # pylint: disable=E1101 # noqa
264
+
265
+ # Context Information:
266
+ @property
267
+ def has_context(self) -> bool:
268
+ """Check if any context (vector or conversation) was used."""
269
+ return self.used_vector_context or self.used_conversation_history
270
+
271
+ @property
272
+ def context_summary(self) -> Dict[str, Any]:
273
+ """Get a summary of context usage."""
274
+ return {
275
+ 'used_vector_context': self.used_vector_context,
276
+ 'used_conversation_history': self.used_conversation_history,
277
+ 'vector_context_length': self.vector_context_length,
278
+ 'conversation_context_length': self.conversation_context_length,
279
+ 'search_results_count': self.search_results_count,
280
+ 'search_type': self.search_type,
281
+ 'context_sources_count': len(self.context_sources) if self.context_sources else 0
282
+ }
283
+
284
+ def set_vector_context_info(
285
+ self,
286
+ used: bool,
287
+ context_length: int = 0,
288
+ search_results_count: int = 0,
289
+ search_type: Optional[str] = None,
290
+ score_threshold: Optional[float] = None,
291
+ sources: Optional[List[str]] = None,
292
+ source_documents: Optional[List[SourceDocument]] = None
293
+ ) -> None:
294
+ """Set vector context information."""
295
+ self.used_vector_context = used
296
+ self.vector_context_length = context_length
297
+ self.search_results_count = search_results_count
298
+ self.search_type = search_type
299
+ self.search_score_threshold = score_threshold
300
+ if source_documents:
301
+ self.source_documents.extend(source_documents)
302
+ if sources:
303
+ self.context_sources.extend(sources) # pylint: disable=E1101 # noqa
304
+
305
+ def set_conversation_context_info(
306
+ self,
307
+ used: bool,
308
+ context_length: int = 0
309
+ ) -> None:
310
+ """Set conversation context information."""
311
+ self.used_conversation_history = used
312
+ self.conversation_context_length = context_length
313
+
314
+ def to_dict(self) -> Dict[str, Any]:
315
+ """Convert to dictionary for serialization."""
316
+ return self.model_dump()
317
+
318
+ def get_context_metadata(self) -> Dict[str, Any]:
319
+ """Get metadata about context usage for logging/analytics."""
320
+ return {
321
+ 'context_usage': {
322
+ 'vector_context': {
323
+ 'used': self.used_vector_context,
324
+ 'length': self.vector_context_length,
325
+ 'search_results': self.search_results_count,
326
+ 'search_type': self.search_type,
327
+ 'score_threshold': self.search_score_threshold,
328
+ 'sources_count': len(self.context_sources) if self.context_sources else 0
329
+ },
330
+ 'conversation_history': {
331
+ 'used': self.used_conversation_history,
332
+ 'length': self.conversation_context_length
333
+ }
334
+ },
335
+ 'tools': {
336
+ 'used': self.has_tools,
337
+ 'count': len(self.tool_calls)
338
+ },
339
+ 'timing': {
340
+ 'created_at': self.created_at.isoformat() # pylint: disable=E1101 # noqa
341
+ }
342
+ }
343
+
344
+ def render_as(
345
+ self,
346
+ mode: OutputMode,
347
+ formatter: Any,
348
+ **kwargs
349
+ ) -> 'AIMessage':
350
+ """
351
+ Create a new AIMessage with different rendering.
352
+ Preserves structured_output.
353
+ """
354
+ source = self.structured_output if self.is_structured else self.output
355
+
356
+ return AIMessage(
357
+ **{
358
+ **self.model_dump(exclude={'output', 'output_mode'}),
359
+ 'output': formatter.format(mode, source, **kwargs),
360
+ 'output_mode': mode
361
+ }
362
+ )
363
+
364
+ # Factory functions to create AIMessage from different providers
365
+ class AIMessageFactory:
366
+ """Factory to create AIMessage from different provider responses."""
367
+
368
+ @staticmethod
369
+ def from_completion(
370
+ response: Any,
371
+ input_text: str,
372
+ structured_output: Optional[Any] = None,
373
+ output_mode: OutputMode = OutputMode.DEFAULT,
374
+ formatter: Optional[Any] = None,
375
+ **kwargs
376
+ ) -> AIMessage:
377
+ """Create AIMessage with proper separation of concerns"""
378
+
379
+ # Determine the raw output
380
+ raw_output = structured_output or response.content
381
+
382
+ # Create base message
383
+ message = AIMessage(
384
+ input=input_text,
385
+ output=raw_output, # Initially set to raw
386
+ structured_output=structured_output,
387
+ is_structured=structured_output is not None,
388
+ output_mode=output_mode,
389
+ **kwargs
390
+ )
391
+
392
+ # Apply formatting if needed
393
+ if output_mode != OutputMode.DEFAULT and formatter:
394
+ # Render from structured_output if available, else from output
395
+ source = message.structured_output if message.is_structured else message.output
396
+ message.output = formatter.format(output_mode, source)
397
+
398
+ return message
399
+
400
+ @staticmethod
401
+ def from_openai(
402
+ response: Any,
403
+ input_text: str,
404
+ model: str,
405
+ user_id: Optional[str] = None,
406
+ session_id: Optional[str] = None,
407
+ turn_id: Optional[str] = None,
408
+ structured_output: Any = None
409
+ ) -> AIMessage:
410
+ """Create AIMessage from OpenAI response."""
411
+ message = response.choices[0].message
412
+
413
+ # Handle tool calls
414
+ tool_calls = []
415
+ if hasattr(message, 'tool_calls') and message.tool_calls:
416
+ tool_calls.extend(
417
+ ToolCall(
418
+ id=tc.id,
419
+ name=tc.function.name,
420
+ arguments=(
421
+ tc.function.arguments
422
+ if isinstance(tc.function.arguments, dict)
423
+ else eval(tc.function.arguments)
424
+ ),
425
+ )
426
+ for tc in message.tool_calls
427
+ )
428
+
429
+ finish_reason = getattr(response.choices[0], "finish_reason", None)
430
+ stop_reason = getattr(response.choices[0], "stop_reason", None)
431
+
432
+ return AIMessage(
433
+ input=input_text,
434
+ output=structured_output or message.content,
435
+ is_structured=structured_output is not None,
436
+ structured_output=structured_output,
437
+ model=model,
438
+ provider="openai",
439
+ usage=CompletionUsage.from_openai(response.usage),
440
+ stop_reason=stop_reason or finish_reason,
441
+ finish_reason=finish_reason,
442
+ tool_calls=tool_calls,
443
+ user_id=user_id,
444
+ session_id=session_id,
445
+ turn_id=turn_id,
446
+ raw_response=response.dict() if hasattr(response, 'dict') else response.__dict__
447
+ )
448
+
449
+ @staticmethod
450
+ def from_groq(
451
+ response: Any,
452
+ input_text: str,
453
+ model: str,
454
+ user_id: Optional[str] = None,
455
+ session_id: Optional[str] = None,
456
+ turn_id: Optional[str] = None,
457
+ structured_output: Any = None
458
+ ) -> AIMessage:
459
+ """Create AIMessage from Groq response."""
460
+ message = response.choices[0].message
461
+
462
+ # Handle tool calls
463
+ tool_calls = []
464
+ if hasattr(message, 'tool_calls') and message.tool_calls:
465
+ tool_calls.extend(
466
+ ToolCall(
467
+ id=tc.id,
468
+ name=tc.function.name,
469
+ arguments=(
470
+ tc.function.arguments
471
+ if isinstance(tc.function.arguments, dict)
472
+ else eval(tc.function.arguments)
473
+ ),
474
+ )
475
+ for tc in message.tool_calls
476
+ )
477
+
478
+ return AIMessage(
479
+ input=input_text,
480
+ output=structured_output or message.content,
481
+ is_structured=structured_output is not None,
482
+ structured_output=structured_output,
483
+ model=model,
484
+ provider="groq",
485
+ usage=CompletionUsage.from_groq(response.usage),
486
+ stop_reason=response.choices[0].finish_reason,
487
+ finish_reason=response.choices[0].finish_reason,
488
+ tool_calls=tool_calls,
489
+ user_id=user_id,
490
+ session_id=session_id,
491
+ turn_id=turn_id,
492
+ raw_response=response.dict() if hasattr(response, 'dict') else response.__dict__,
493
+ response=message.content
494
+ )
495
+
496
+ @staticmethod
497
+ def from_grok(
498
+ response: Any,
499
+ input_text: str,
500
+ model: str,
501
+ user_id: Optional[str] = None,
502
+ session_id: Optional[str] = None,
503
+ turn_id: Optional[str] = None,
504
+ structured_output: Any = None
505
+ ) -> AIMessage:
506
+ """Create AIMessage from Grok response."""
507
+ # xAI SDK response structure needs verification, assuming OpenAI-like for now based on 'usage'
508
+ # If response is a Pydantic model from structured output, content might be parsed.
509
+
510
+ content = ""
511
+ tool_calls = []
512
+ finish_reason = None
513
+
514
+ # Check if it's a simple text response or object
515
+ if hasattr(response, 'content'):
516
+ content = response.content
517
+ elif isinstance(response, dict) and 'content' in response:
518
+ content = response['content']
519
+
520
+ # Try to extract standard fields if present
521
+ if hasattr(response, 'tool_calls') and response.tool_calls:
522
+ # Map tool calls
523
+ tool_calls.extend(
524
+ ToolCall(
525
+ id=tc.id,
526
+ name=tc.function.name,
527
+ arguments=(
528
+ tc.function.arguments
529
+ if isinstance(tc.function.arguments, dict)
530
+ else eval(tc.function.arguments)
531
+ ),
532
+ )
533
+ for tc in response.tool_calls
534
+ )
535
+
536
+ return AIMessage(
537
+ input=input_text,
538
+ output=structured_output or content,
539
+ is_structured=structured_output is not None,
540
+ structured_output=structured_output,
541
+ model=model,
542
+ provider="grok",
543
+ usage=CompletionUsage.from_grok(response.usage) if hasattr(response, 'usage') else CompletionUsage(),
544
+ stop_reason=finish_reason,
545
+ finish_reason=finish_reason,
546
+ tool_calls=tool_calls,
547
+ user_id=user_id,
548
+ session_id=session_id,
549
+ turn_id=turn_id,
550
+ raw_response=response.__dict__ if hasattr(response, '__dict__') else response,
551
+ response=content
552
+ )
553
+
554
+ @staticmethod
555
+ def from_claude(
556
+ response: Dict[str, Any],
557
+ input_text: str,
558
+ model: str,
559
+ user_id: Optional[str] = None,
560
+ session_id: Optional[str] = None,
561
+ turn_id: Optional[str] = None,
562
+ structured_output: Any = None,
563
+ tool_calls: List[ToolCall] = None
564
+ ) -> AIMessage:
565
+ """Create AIMessage from Claude response."""
566
+ # Extract text content
567
+ content = ""
568
+ for content_block in response.get("content", []):
569
+ if content_block.get("type") == "text":
570
+ content += content_block.get("text", "")
571
+
572
+ return AIMessage(
573
+ input=input_text,
574
+ output=structured_output or content,
575
+ is_structured=structured_output is not None,
576
+ structured_output=structured_output,
577
+ model=model,
578
+ provider="claude",
579
+ usage=CompletionUsage.from_claude(response.get("usage", {})),
580
+ stop_reason=response.get("stop_reason"),
581
+ finish_reason=response.get("stop_reason"),
582
+ tool_calls=tool_calls or [],
583
+ user_id=user_id,
584
+ session_id=session_id,
585
+ turn_id=turn_id,
586
+ raw_response=response,
587
+ response=content if isinstance(content, str) else str(content)
588
+ )
589
+
590
+ @staticmethod
591
+ def from_gemini(
592
+ response: Any,
593
+ input_text: str,
594
+ model: str,
595
+ user_id: Optional[str] = None,
596
+ session_id: Optional[str] = None,
597
+ turn_id: Optional[str] = None,
598
+ structured_output: Any = None,
599
+ tool_calls: List[ToolCall] = None,
600
+ # Add these new parameters:
601
+ conversation_history: Optional[Any] = None,
602
+ text_response: Optional[str] = None,
603
+ files: Optional[List[Path]] = None
604
+ ) -> AIMessage:
605
+ """Create AIMessage from Gemini/Vertex AI response."""
606
+ # Handle both direct text responses and response objects
607
+ if text_response:
608
+ content = text_response
609
+ elif hasattr(response, 'text'):
610
+ content = response.text
611
+ else:
612
+ content = str(response)
613
+
614
+ # Extract usage information
615
+ usage_dict = {}
616
+ if hasattr(response, 'usage_metadata') and response.usage_metadata:
617
+ # Vertex AI format
618
+ usage_dict = {
619
+ 'prompt_token_count': response.usage_metadata.prompt_token_count,
620
+ 'candidates_token_count': response.usage_metadata.candidates_token_count,
621
+ 'total_token_count': response.usage_metadata.total_token_count
622
+ }
623
+ elif hasattr(response, 'usage'):
624
+ # Standard Gemini API format
625
+ usage_dict = response.usage.__dict__ if hasattr(response.usage, '__dict__') else {}
626
+
627
+ ai_message = AIMessage(
628
+ input=input_text,
629
+ output=structured_output or content,
630
+ is_structured=structured_output is not None,
631
+ structured_output=structured_output,
632
+ model=model,
633
+ provider="gemini", # Will be overridden to "vertex_ai" in VertexAIClient
634
+ usage=CompletionUsage.from_gemini(usage_dict),
635
+ stop_reason="completed",
636
+ finish_reason="completed",
637
+ tool_calls=tool_calls or [],
638
+ user_id=user_id,
639
+ session_id=session_id,
640
+ turn_id=turn_id,
641
+ raw_response=response.__dict__ if hasattr(response, '__dict__') else str(response),
642
+ response=content,
643
+ files=files or [],
644
+ )
645
+
646
+ if conversation_history:
647
+ ai_message.used_conversation_history = True
648
+ ai_message.conversation_context_length = len(conversation_history.turns) if hasattr(conversation_history, 'turns') else 0
649
+
650
+ return ai_message
651
+
652
+ @staticmethod
653
+ def create_message(
654
+ response: Any,
655
+ input_text: str,
656
+ model: str,
657
+ user_id: Optional[str] = None,
658
+ session_id: Optional[str] = None,
659
+ turn_id: Optional[str] = None,
660
+ structured_output: Any = None,
661
+ tool_calls: List[ToolCall] = None,
662
+ # Add these new parameters:
663
+ conversation_history: Optional[Any] = None,
664
+ text_response: Optional[str] = None,
665
+ usage: Optional[CompletionUsage] = None,
666
+ response_time: Optional[float] = None
667
+ ) -> AIMessage:
668
+ """Create AIMessage from any provider response."""
669
+ if hasattr(response, 'provider'):
670
+ provider = response.provider.lower()
671
+ else:
672
+ provider = "unknown"
673
+
674
+ ai_message = AIMessage(
675
+ input=input_text,
676
+ output=structured_output,
677
+ is_structured=structured_output is not None,
678
+ structured_output=structured_output,
679
+ model=model,
680
+ provider=provider,
681
+ stop_reason="completed",
682
+ finish_reason="completed",
683
+ tool_calls=tool_calls or [],
684
+ user_id=user_id,
685
+ session_id=session_id,
686
+ turn_id=turn_id,
687
+ raw_response=response.__dict__ if hasattr(response, '__dict__') else {"result": text_response},
688
+ response=text_response,
689
+ usage=usage or CompletionUsage.from_response(response),
690
+ response_time=response_time
691
+ )
692
+ if conversation_history:
693
+ ai_message.used_conversation_history = True
694
+ ai_message.conversation_context_length = len(conversation_history.turns) if hasattr(conversation_history, 'turns') else 0
695
+
696
+ return ai_message
697
+
698
+ @staticmethod
699
+ def from_imagen(**kwargs):
700
+ return AIMessage(**kwargs)
701
+
702
+ @staticmethod
703
+ def from_speech(**kwargs):
704
+ return AIMessage(**kwargs)
705
+
706
+ @staticmethod
707
+ def from_video(**kwargs):
708
+ return AIMessage(**kwargs)
709
+
710
+ class AgentResponse(BaseModel):
711
+ """
712
+ AgentResponse is a model that defines the structure of the response for Any Parrot agent.
713
+ """
714
+ session_id: Optional[str] = Field(
715
+ default=None,
716
+ description="Unique identifier for the session"
717
+ )
718
+ user_id: Optional[str] = Field(
719
+ default=None,
720
+ description="Unique identifier for the user"
721
+ )
722
+ agent_id: Optional[str] = Field(
723
+ default=None,
724
+ description="Unique identifier for the agent that processed the request"
725
+ )
726
+ agent_name: Optional[str] = Field(
727
+ default="Agentic",
728
+ description="Name of the agent that processed the request"
729
+ )
730
+ status: str = Field(default="success", description="Status of the response")
731
+ question: Optional[str] = Field(
732
+ default=None,
733
+ description="Question made by User"
734
+ )
735
+ response: Optional[AIMessage] = Field(
736
+ default=None,
737
+ description="Response returned by the agent"
738
+ )
739
+ data: Optional[str] = Field(
740
+ default=None,
741
+ description="Data returned by the agent, can be text, JSON, etc."
742
+ )
743
+ # Optional output field for structured data
744
+ output: Optional[Any] = Field(
745
+ default=None,
746
+ description="Output of the agent's processing"
747
+ )
748
+ attributes: Dict[str, str] = Field(
749
+ default_factory=dict,
750
+ description="Attributes associated with the response"
751
+ )
752
+ # Timestamp
753
+ created_at: datetime = Field(
754
+ default_factory=datetime.now, description="Timestamp when response was created"
755
+ )
756
+ # Optional file paths
757
+ transcript: Optional[str] = Field(
758
+ default=None, description="Transcript of the conversation with the agent"
759
+ )
760
+ script_path: Optional[str] = Field(
761
+ default=None, description="Path to the conversational script associated with the session"
762
+ )
763
+ podcast_path: Optional[str] = Field(
764
+ default=None, description="Path to the podcast associated with the session"
765
+ )
766
+ pdf_path: Optional[str] = Field(
767
+ default=None, description="Path to the PDF associated with the session"
768
+ )
769
+ document_path: Optional[str] = Field(
770
+ default=None, description="Path to any document generated during session"
771
+ )
772
+ images: Optional[List[Path]] = Field(
773
+ default_factory=list,
774
+ description="List of image file paths generated by the model"
775
+ )
776
+ media: Optional[List[Path]] = Field(
777
+ default_factory=list,
778
+ description="List of media file paths generated by the model"
779
+ )
780
+ documents: Optional[List[Any]] = Field(
781
+ default_factory=list,
782
+ description="List of document file paths associated with the response"
783
+ )
784
+ # complete list of generated files:
785
+ files: List[str] = Field(
786
+ default_factory=list,
787
+ description="List of files generated during the session"
788
+ )
789
+ turn_id: Optional[str] = Field(
790
+ default=None, description="Unique identifier for the conversation turn"
791
+ )
792
+
793
+ class Config:
794
+ """Pydantic configuration for AgentResponse."""
795
+ # Allow arbitrary types for output field (pandas DataFrames, etc.)
796
+ arbitrary_types_allowed = False
797
+ # Allow extra fields if needed
798
+ extra = "allow"
799
+ # Use enum values
800
+ use_enum_values = True
801
+ # Validate assignment
802
+ validate_assignment = True
803
+
804
+ @property
805
+ def content(self) -> Any:
806
+ """
807
+ Get content as a string. This is an alias for to_text property
808
+ that provides a more intuitive API and compatibility with standard
809
+ AI message formats.
810
+
811
+ Returns:
812
+ str: The text representation of the output
813
+ """
814
+ return self.output
815
+
816
+ @content.setter
817
+ def content(self, value: Any) -> None:
818
+ """
819
+ Set content by updating the output field.
820
+
821
+ Args:
822
+ value: The content string to set
823
+ """
824
+ self.output = value
825
+
826
+ @model_validator(mode='after')
827
+ def sync_documents_and_paths(self):
828
+ """
829
+ Automatically populate documents list from individual path fields
830
+ and sync documents from AIMessage if present.
831
+ """
832
+ # Sync from AIMessage if it exists
833
+ if self.response and isinstance(self.response, AIMessage):
834
+ if hasattr(self.response, 'documents') and self.response.documents:
835
+ # Add AIMessage documents to our documents list
836
+ for doc in self.response.documents:
837
+ if doc not in self.documents:
838
+ self.documents.append(doc)
839
+
840
+ if hasattr(self.response, 'files') and self.response.files:
841
+ # Add AIMessage files to our files list
842
+ for file in self.response.files:
843
+ if file not in self.files:
844
+ self.files.append(file)
845
+
846
+ if hasattr(self.response, 'images') and self.response.images:
847
+ # Add AIMessage images to our images list
848
+ for img in self.response.images:
849
+ if img not in self.images:
850
+ self.images.append(img)
851
+
852
+ if hasattr(self.response, 'media') and self.response.media:
853
+ # Add AIMessage media to our media list
854
+ for med in self.response.media:
855
+ if med not in self.media:
856
+ self.media.append(med)
857
+
858
+ # Auto-add individual paths to appropriate collections
859
+ path_mappings = [
860
+ (self.script_path, self.documents, 'script'),
861
+ (self.podcast_path, self.files, 'podcast'),
862
+ (self.pdf_path, self.documents, 'pdf'),
863
+ (self.document_path, self.documents, 'document'),
864
+ ]
865
+
866
+ for path, collection, path_type in path_mappings:
867
+ if path and path not in collection:
868
+ collection.append(path)
869
+
870
+ return self
871
+
872
+ def add_document(self, path: Union[str, Path]) -> None:
873
+ """
874
+ Add a document to the documents list also document path.
875
+
876
+ Args:
877
+ path: Path to the document
878
+ """
879
+ path_str = str(path)
880
+ if path_str not in self.documents:
881
+ self.documents.append(path_str)
882
+ self.document_path = path_str
883
+ self.add_file(path_str)
884
+
885
+ def add_file(self, path: Union[str, Path]) -> None:
886
+ """
887
+ Add a file to the files list.
888
+
889
+ Args:
890
+ path: Path to the file
891
+ """
892
+ path_str = str(path)
893
+ if path_str not in self.files:
894
+ self.files.append(path_str)
895
+
896
+ def add_image(self, path: Union[str, Path]) -> None:
897
+ """
898
+ Add an image to the images list.
899
+
900
+ Args:
901
+ path: Path to the image
902
+ """
903
+ path_str = str(path)
904
+ if path_str not in self.images:
905
+ self.images.append(path_str)
906
+ self.add_file(path_str)
907
+
908
+ def add_media(self, path: Union[str, Path]) -> None:
909
+ """
910
+ Add a media file to the media list.
911
+
912
+ Args:
913
+ path: Path to the media file
914
+ """
915
+ path_str = str(path)
916
+ if path_str not in self.media:
917
+ self.media.append(path_str)
918
+ self.add_file(path_str)
919
+
920
+ def set_podcast_path(self, path: Union[str, Path]) -> None:
921
+ """
922
+ Set the podcast_path and automatically add to files list.
923
+
924
+ Args:
925
+ path: Path to the podcast
926
+ """
927
+ self.podcast_path = str(path)
928
+ self.add_file(path)
929
+
930
+ def set_pdf_path(self, path: Union[str, Path]) -> None:
931
+ """
932
+ Set the pdf_path and automatically add to documents list.
933
+
934
+ Args:
935
+ path: Path to the PDF
936
+ """
937
+ self.pdf_path = str(path)
938
+ self.add_document(path)