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,518 @@
1
+ # parrot/tools/powerbi.py
2
+ from __future__ import annotations
3
+ from typing import Any, Dict, List, Optional, Union
4
+ import os
5
+ import asyncio
6
+ import random
7
+ import logging
8
+ import io
9
+ import csv
10
+ import time
11
+ import requests
12
+ import aiohttp
13
+ import pyarrow as pa
14
+ import pyarrow.parquet as pq
15
+ import pandas as pd
16
+ from pydantic import BaseModel, Field, model_validator, PrivateAttr, ConfigDict
17
+ from .abstract import AbstractTool, AbstractToolArgsSchema, ToolResult
18
+
19
+ POWERBI_BASE_URL = os.getenv("POWERBI_BASE_URL", "https://api.powerbi.com/v1.0/myorg")
20
+ PBI_SCOPE = "https://analysis.windows.net/powerbi/api/.default"
21
+
22
+ # ---------------- Utilities ----------------
23
+
24
+ def _fix_table_name(table: str) -> str:
25
+ """Add single quotes around table names that contain spaces (DAX)."""
26
+ t = (table or "").strip()
27
+ if " " in t and not (t.startswith("'") and t.endswith("'")):
28
+ return f"'{t}'"
29
+ return t
30
+
31
+ def _json_rows_to_markdown(rows: List[Dict[str, Any]], table_name: Optional[str] = None) -> str:
32
+ if not rows:
33
+ return ""
34
+ headers = list(rows[0].keys())
35
+
36
+ def clean(h: str) -> str:
37
+ h2 = h.replace("[", ".").replace("]", "")
38
+ if table_name:
39
+ pref = f"{table_name}."
40
+ if h2.startswith(pref):
41
+ return h2[len(pref):]
42
+ return h2
43
+
44
+ hdrs = [clean(h) for h in headers]
45
+ out = "|" + "|".join(f" {h} " for h in hdrs) + "|\n"
46
+ out += "|" + "|".join("---" for _ in hdrs) + "|\n"
47
+ for row in rows:
48
+ out += "|" + "|".join(f" {row.get(h, '')} " for h in headers) + "|\n"
49
+ return out
50
+
51
+ def _rows_to_csv_string(rows: List[Dict[str, Any]]) -> str:
52
+ if not rows:
53
+ return ""
54
+ output = io.StringIO()
55
+ writer = csv.DictWriter(output, fieldnames=list(rows[0].keys()))
56
+ writer.writeheader()
57
+ writer.writerows(rows)
58
+ return output.getvalue()
59
+
60
+ def _rows_to_dataframe(rows: List[Dict[str, Any]]):
61
+ return pd.DataFrame(rows)
62
+
63
+ # near other helpers at the top
64
+ def _rows_to_arrow_table(rows: List[Dict[str, Any]]):
65
+ # pyarrow infers schema well from pylist of dicts
66
+ return pa.Table.from_pylist(rows)
67
+
68
+ def _write_parquet(rows: List[Dict[str, Any]], path: str) -> str:
69
+ # prefer pyarrow directly; fallback to pandas if pyarrow not available
70
+ try:
71
+ table = pa.Table.from_pylist(rows)
72
+ pq.write_table(table, path)
73
+ return path
74
+ except ImportError:
75
+ # fallback via pandas (requires pandas + either pyarrow or fastparquet installed)
76
+ df = pd.DataFrame(rows)
77
+ df.to_parquet(path) # pandas will pick available parquet engine
78
+ return path
79
+
80
+
81
+ # ---------------- Core Client ----------------
82
+ class PowerBIDatasetClient(BaseModel):
83
+ """Client for executing DAX queries against a Power BI dataset."""
84
+ model_config = ConfigDict(arbitrary_types_allowed=True)
85
+ _logger: logging.Logger = PrivateAttr(default=logging.getLogger("PowerBIDatasetClient"))
86
+
87
+ dataset_id: str
88
+ table_names: List[str] = Field(default_factory=list)
89
+ group_id: Optional[str] = None
90
+
91
+ # Auth
92
+ token: Optional[str] = None
93
+ credential: Optional[Any] = None # azure.core.credentials.TokenCredential
94
+ impersonated_user_name: Optional[str] = None
95
+
96
+ # Sampling for table info
97
+ sample_rows_in_table_info: int = Field(default=1, gt=0, le=10)
98
+
99
+ # Retry settings (NEW)
100
+ max_attempts: int = Field(default=5, ge=1, le=10)
101
+ base_backoff: float = Field(default=0.5, ge=0.0) # seconds
102
+ max_backoff: float = Field(default=10.0, ge=0.0) # seconds
103
+
104
+ _schemas: Dict[str, str] = PrivateAttr(default_factory=dict)
105
+ _aiosession: Optional[aiohttp.ClientSession] = PrivateAttr(default=None)
106
+
107
+ @model_validator(mode="before")
108
+ @classmethod
109
+ def _validate_auth(cls, v: Dict[str, Any]) -> Dict[str, Any]:
110
+ if not v.get("token") and not v.get("credential"):
111
+ raise ValueError("Please provide either a credential or a token.")
112
+ return v
113
+
114
+ @property
115
+ def request_url(self) -> str:
116
+ if self.group_id:
117
+ return f"{POWERBI_BASE_URL}/groups/{self.group_id}/datasets/{self.dataset_id}/executeQueries"
118
+ return f"{POWERBI_BASE_URL}/datasets/{self.dataset_id}/executeQueries"
119
+
120
+ def _headers(self) -> Dict[str, str]:
121
+ if self.token:
122
+ return {"Content-Type": "application/json", "Authorization": f"Bearer {self.token}"}
123
+ try:
124
+ token = self.credential.get_token(PBI_SCOPE).token # type: ignore[attr-defined]
125
+ return {"Content-Type": "application/json", "Authorization": f"Bearer {token}"}
126
+ except Exception as exc:
127
+ raise RuntimeError("Could not get a token from the supplied credentials") from exc
128
+
129
+ def _payload(self, command: str) -> Dict[str, Any]:
130
+ return {
131
+ "queries": [{"query": command}],
132
+ "impersonatedUserName": self.impersonated_user_name,
133
+ "serializerSettings": {"includeNulls": True},
134
+ }
135
+
136
+ # ---------- Retry helpers (NEW) ----------
137
+
138
+ def _compute_sleep(self, attempt: int, retry_after: Optional[float]) -> float:
139
+ if retry_after is not None:
140
+ return min(retry_after, self.max_backoff)
141
+ # exponential backoff with jitter
142
+ backoff = min(self.base_backoff * (2 ** (attempt - 1)), self.max_backoff)
143
+ jitter = random.uniform(0, backoff / 2)
144
+ return backoff + jitter
145
+
146
+ # ---------- Sync ----------
147
+
148
+ def run(self, command: str, timeout: int = 30) -> Dict[str, Any]:
149
+ headers = self._headers()
150
+ payload = self._payload(command)
151
+
152
+ attempt = 1
153
+ while True:
154
+ resp = requests.post(self.request_url, json=payload, headers=headers, timeout=timeout)
155
+ status = resp.status_code
156
+ if status == 403:
157
+ return {"error": "TokenError: Could not login to Power BI (403)."}
158
+
159
+ if status < 400:
160
+ try:
161
+ return resp.json()
162
+ except Exception as exc:
163
+ raise RuntimeError(f"Invalid JSON from Power BI: {exc}") from exc
164
+
165
+ # retry on 429/5xx
166
+ if status in {429, 500, 502, 503, 504} and attempt < self.max_attempts:
167
+ retry_after_header = resp.headers.get("Retry-After")
168
+ retry_after = None
169
+ if retry_after_header:
170
+ try:
171
+ retry_after = float(retry_after_header)
172
+ except ValueError:
173
+ retry_after = None
174
+ sleep_s = self._compute_sleep(attempt, retry_after)
175
+ self._logger.warning(
176
+ "PowerBI %s; retrying in %.2fs (attempt %d/%d)", status, sleep_s, attempt, self.max_attempts
177
+ )
178
+ time.sleep(sleep_s)
179
+ attempt += 1
180
+ continue
181
+
182
+ # give up
183
+ try:
184
+ err = resp.json()
185
+ except Exception:
186
+ err = {"message": resp.text}
187
+ return {"error": f"HTTP {status}", "details": err}
188
+
189
+ # ---------- Async ----------
190
+ async def arun(self, command: str, timeout: int = 30) -> Dict[str, Any]:
191
+ headers = self._headers()
192
+ payload = self._payload(command)
193
+
194
+ attempt = 1
195
+ while True:
196
+ if self._aiosession:
197
+ async with self._aiosession.post(
198
+ self.request_url, headers=headers, json=payload, timeout=timeout
199
+ ) as resp:
200
+ status = resp.status
201
+ if status == 403:
202
+ return {"error": "TokenError: Could not login to Power BI (403)."}
203
+ if status < 400:
204
+ return await resp.json(content_type=resp.content_type)
205
+ # retry on 429/5xx
206
+ if status in (429, 500, 502, 503, 504) and attempt < self.max_attempts:
207
+ retry_after_header = resp.headers.get("Retry-After")
208
+ retry_after = None
209
+ if retry_after_header:
210
+ try:
211
+ retry_after = float(retry_after_header)
212
+ except ValueError:
213
+ retry_after = None
214
+ sleep_s = self._compute_sleep(attempt, retry_after)
215
+ self._logger.warning(
216
+ "PowerBI %s; retrying in %.2fs (attempt %d/%d)", status, sleep_s, attempt, self.max_attempts
217
+ )
218
+ await asyncio.sleep(sleep_s)
219
+ attempt += 1
220
+ continue
221
+
222
+ try:
223
+ err = await resp.json(content_type=resp.content_type)
224
+ except Exception:
225
+ err = {"message": await resp.text()}
226
+ return {"error": f"HTTP {status}", "details": err}
227
+
228
+ async with aiohttp.ClientSession() as session:
229
+ async with session.post(self.request_url, headers=headers, json=payload, timeout=timeout) as resp:
230
+ status = resp.status
231
+ if status == 403:
232
+ return {"error": "TokenError: Could not login to Power BI (403)."}
233
+ if status < 400:
234
+ return await resp.json(content_type=resp.content_type)
235
+ if status in (429, 500, 502, 503, 504) and attempt < self.max_attempts:
236
+ retry_after_header = resp.headers.get("Retry-After")
237
+ retry_after = None
238
+ if retry_after_header:
239
+ try:
240
+ retry_after = float(retry_after_header)
241
+ except ValueError:
242
+ retry_after = None
243
+ sleep_s = self._compute_sleep(attempt, retry_after)
244
+ self._logger.warning("PowerBI %s; retrying in %.2fs (attempt %d/%d)", status, sleep_s, attempt, self.max_attempts)
245
+ await asyncio.sleep(sleep_s)
246
+ attempt += 1
247
+ continue
248
+ try:
249
+ err = await resp.json(content_type=resp.content_type)
250
+ except Exception:
251
+ err = {"message": await resp.text()}
252
+ return {"error": f"HTTP {status}", "details": err}
253
+
254
+ # ---------- Schema-like preview (TOPN sampling) ----------
255
+ def get_table_info(self, tables: Optional[Union[str, List[str]]] = None) -> str:
256
+ requested = self._normalize_tables(tables)
257
+ if not requested:
258
+ return "No (valid) tables requested."
259
+ todo = [t for t in requested if t not in self._schemas]
260
+ for t in todo:
261
+ try:
262
+ js = self.run(f"EVALUATE TOPN({self.sample_rows_in_table_info}, {t})")
263
+ rows = (js or {}).get("results", [{}])[0].get("tables", [{}])[0].get("rows", [])
264
+ self._schemas[t] = _json_rows_to_markdown(rows, table_name=t.strip("'"))
265
+ except Exception as exc:
266
+ self._logger.warning("Error while getting table info for %s: %s", t, exc)
267
+ self._schemas[t] = "unknown"
268
+ return ", ".join([self._schemas.get(t, "unknown") for t in requested])
269
+
270
+ async def aget_table_info(self, tables: Optional[Union[str, List[str]]] = None) -> str:
271
+ requested = self._normalize_tables(tables)
272
+ if not requested:
273
+ return "No (valid) tables requested."
274
+ todo = [t for t in requested if t not in self._schemas]
275
+
276
+ async def _fetch(t: str) -> None:
277
+ try:
278
+ js = await self.arun(f"EVALUATE TOPN({self.sample_rows_in_table_info}, {t})")
279
+ rows = (js or {}).get("results", [{}])[0].get("tables", [{}])[0].get("rows", [])
280
+ self._schemas[t] = _json_rows_to_markdown(rows, table_name=t.strip("'"))
281
+ except Exception as exc:
282
+ self._logger.warning("Error while getting table info for %s: %s", t, exc)
283
+ self._schemas[t] = "unknown"
284
+
285
+ await asyncio.gather(*[_fetch(t) for t in todo])
286
+ return ", ".join([self._schemas.get(t, "unknown") for t in requested])
287
+
288
+ def _normalize_tables(self, tables: Optional[Union[str, List[str]]]) -> Optional[List[str]]:
289
+ if tables is None:
290
+ # FIX: no starred-unpack! Just map.
291
+ return [_fix_table_name(t) for t in self.table_names] if self.table_names else None
292
+ if isinstance(tables, str):
293
+ t = _fix_table_name(tables)
294
+ if self.table_names and t not in [_fix_table_name(x) for x in self.table_names]:
295
+ self.logger.warning("Table %s not found in dataset.", tables)
296
+ return None
297
+ return [t]
298
+ if isinstance(tables, list):
299
+ fixed = [_fix_table_name(x) for x in tables if x]
300
+ if self.table_names:
301
+ known = {_fix_table_name(x) for x in self.table_names}
302
+ fixed = [t for t in fixed if t in known]
303
+ if not fixed:
304
+ self._logger.warning("No valid tables found in requested list.")
305
+ return None
306
+ return fixed or None
307
+ return None
308
+
309
+ class _BasePowerBIToolArgs(AbstractToolArgsSchema):
310
+ """Base arguments for Power BI tools."""
311
+ dataset_id: str = Field(..., description="Power BI dataset (semantic model) ID")
312
+ group_id: Optional[str] = Field(None, description="Workspace (group) ID; omit for My workspace")
313
+ token: Optional[str] = Field(None, description="Bearer token (if not using Azure TokenCredential)")
314
+ impersonated_user_name: Optional[str] = Field(None, description="UPN to impersonate for RLS testing")
315
+ table_names: Optional[List[str]] = Field(default=None, description="Known table names for validation/preview")
316
+ sample_rows_in_table_info: int = Field(default=1, ge=1, le=10, description="Rows sampled in table preview")
317
+ timeout: int = Field(default=30, ge=1, le=300, description="HTTP timeout in seconds")
318
+
319
+ # Retry knobs
320
+ max_attempts: int = Field(default=5, ge=1, le=10, description="Max HTTP attempts on 429/5xx")
321
+ base_backoff: float = Field(default=0.5, ge=0.0, description="Base backoff seconds")
322
+ max_backoff: float = Field(default=10.0, ge=0.0, description="Max backoff seconds")
323
+
324
+ # Export knobs
325
+ export_csv: bool = Field(default=False, description="Write result rows to CSV")
326
+ export_csv_path: Optional[str] = Field(default=None, description="CSV path; if not provided, a temp name is used")
327
+ export_pandas: bool = Field(default=False, description="Return a pandas DataFrame in result (requires pandas)")
328
+
329
+ # DAX templating
330
+ template: Optional[str] = Field(default=None, description="DAX template using Python format syntax")
331
+ parameters: Optional[Dict[str, Any]] = Field(default=None, description="Values for template placeholders")
332
+
333
+ output_format: Optional[str] = Field(
334
+ default=None,
335
+ description="One of: row|rows|json|csv|dataframe|markdown|parquet|pyarrow.Table"
336
+ )
337
+ parquet_path: Optional[str] = Field(
338
+ default=None,
339
+ description="Where to write parquet if output_format='parquet'. Defaults to /tmp/..."
340
+ )
341
+
342
+
343
+ class PowerBIQueryArgs(_BasePowerBIToolArgs):
344
+ """Arguments for PowerBIQueryTool."""
345
+ command: Optional[str] = Field(
346
+ default=None,
347
+ description="DAX command to execute; ignored if 'template' is provided"
348
+ )
349
+
350
+
351
+ class PowerBIQueryTool(AbstractTool):
352
+ """
353
+ Tool for executing DAX queries against a Power BI dataset.
354
+ """
355
+ name = "powerbi_query"
356
+ description = "Execute DAX against a Power BI dataset and return rows"
357
+ args_schema = PowerBIQueryArgs
358
+
359
+ async def _execute(self, **kwargs) -> Any:
360
+ cred = kwargs.get("credential", None)
361
+ client = PowerBIDatasetClient(
362
+ dataset_id=kwargs["dataset_id"],
363
+ group_id=kwargs.get("group_id"),
364
+ token=kwargs.get("token"),
365
+ credential=cred,
366
+ impersonated_user_name=kwargs.get("impersonated_user_name"),
367
+ table_names=kwargs.get("table_names") or [],
368
+ sample_rows_in_table_info=kwargs.get("sample_rows_in_table_info", 1),
369
+ max_attempts=kwargs.get("max_attempts", 5),
370
+ base_backoff=kwargs.get("base_backoff", 0.5),
371
+ max_backoff=kwargs.get("max_backoff", 10.0),
372
+ )
373
+
374
+ # ---- DAX templating (NEW)
375
+ command = kwargs.get("command")
376
+ template = kwargs.get("template")
377
+ params = kwargs.get("parameters") or {}
378
+ if template:
379
+ try:
380
+ # Users can escape literal braces with {{ and }}
381
+ command = template.format(**params)
382
+ except KeyError as exc:
383
+ return ToolResult(status="error", result=None, error=f"Missing template parameter: {exc}")
384
+
385
+ if not command:
386
+ return ToolResult(
387
+ status="error", result=None, error="No DAX command provided (command/template missing)"
388
+ )
389
+
390
+ js = await client.arun(command, timeout=kwargs.get("timeout", 30))
391
+ if "error" in js:
392
+ return ToolResult(
393
+ status="error", result=None, error=js["error"]
394
+ )
395
+
396
+ rows = (js or {}).get("results", [{}])[0].get("tables", [{}])[0].get("rows", [])
397
+ md = _json_rows_to_markdown(rows)
398
+ fmt_raw = kwargs.get("output_format")
399
+ fmt = (fmt_raw or "").strip().lower() if fmt_raw else None
400
+
401
+ # ---- Exports
402
+ if not fmt:
403
+ if kwargs.get("export_csv"):
404
+ fmt = "csv"
405
+ elif kwargs.get("export_pandas"):
406
+ fmt = "dataframe"
407
+
408
+ # Normalize a few aliases
409
+ if fmt in ("row", "json"):
410
+ fmt = "rows"
411
+ if fmt in ("pyarrow", "arrow", "pyarrow.table"):
412
+ fmt = "pyarrow.Table"
413
+
414
+ result_payload: Dict[str, Any] = {
415
+ "raw": js,
416
+ "format": fmt or "default",
417
+ }
418
+
419
+ csv_path = None
420
+ df_obj = None
421
+ parquet_path = None
422
+
423
+ if fmt == "rows" or fmt is None:
424
+ # default behavior keeps both rows and markdown available
425
+ result_payload |= {
426
+ "rows": rows,
427
+ "markdown": md,
428
+ }
429
+
430
+ elif fmt == "markdown":
431
+ result_payload["markdown"] = md
432
+
433
+ elif fmt == "csv":
434
+ csv_text = _rows_to_csv_string(rows)
435
+ path = kwargs.get("export_csv_path") or f"/tmp/powerbi_{client.dataset_id[:8]}_{int(time.time())}.csv"
436
+ with open(path, "w", encoding="utf-8", newline="") as f:
437
+ f.write(csv_text)
438
+ csv_path = path
439
+ result_payload |= {
440
+ "csv_path": csv_path,
441
+ "csv_text": csv_text,
442
+ }
443
+
444
+ elif fmt == "dataframe":
445
+ try:
446
+ df_obj = _rows_to_dataframe(rows)
447
+ except Exception as exc:
448
+ return ToolResult(status="error", result=None, error=str(exc))
449
+ result_payload["dataframe"] = df_obj # note: not JSON-serializable
450
+
451
+ elif fmt == "parquet":
452
+ path = kwargs.get("parquet_path") or f"/tmp/powerbi_{client.dataset_id[:8]}_{int(time.time())}.parquet"
453
+ try:
454
+ parquet_path = _write_parquet(rows, path)
455
+ except Exception as exc:
456
+ return ToolResult(status="error", result=None, error=str(exc))
457
+ result_payload["parquet_path"] = parquet_path
458
+
459
+ elif fmt == "pyarrow.Table":
460
+ try:
461
+ table = _rows_to_arrow_table(rows)
462
+ except Exception as exc:
463
+ return ToolResult(status="error", result=None, error=str(exc))
464
+ result_payload["pyarrow_table"] = table # note: not JSON-serializable
465
+
466
+ else:
467
+ # Unknown selector → return a helpful error
468
+ return ToolResult(
469
+ status="error",
470
+ result=None,
471
+ error=f"Unsupported output_format='{fmt_raw}'. Use one of: row|rows|json|csv|dataframe|markdown|parquet|pyarrow.Table"
472
+ )
473
+
474
+ return {
475
+ "status": "success",
476
+ "result": result_payload
477
+ }
478
+
479
+
480
+ class PowerBITableInfoArgs(_BasePowerBIToolArgs):
481
+ tables: Optional[Union[str, List[str]]] = Field(
482
+ default=None,
483
+ description="Specific table(s) to preview; defaults to known list if provided"
484
+ )
485
+
486
+
487
+ class PowerBITableInfoTool(AbstractTool):
488
+ """
489
+ Tool for previewing table info (sample rows) from a Power BI dataset.
490
+ """
491
+ name = "powerbi_table_info"
492
+ description = "Preview table info (sample rows) for a Power BI dataset"
493
+ args_schema = PowerBITableInfoArgs
494
+
495
+ async def _execute(self, **kwargs) -> Any:
496
+ cred = kwargs.get("credential", None)
497
+ client = PowerBIDatasetClient(
498
+ dataset_id=kwargs["dataset_id"],
499
+ group_id=kwargs.get("group_id"),
500
+ token=kwargs.get("token"),
501
+ credential=cred,
502
+ impersonated_user_name=kwargs.get("impersonated_user_name"),
503
+ table_names=kwargs.get("table_names") or [],
504
+ sample_rows_in_table_info=kwargs.get("sample_rows_in_table_info", 1),
505
+ max_attempts=kwargs.get("max_attempts", 5),
506
+ base_backoff=kwargs.get("base_backoff", 0.5),
507
+ max_backoff=kwargs.get("max_backoff", 10.0),
508
+ )
509
+ md = await client.aget_table_info(kwargs.get("tables"))
510
+ # Optional export of the preview as CSV/DF by stitching rows from TOPN calls is not included here,
511
+ # since get_table_info returns a joined markdown snapshot of multiple tables. If you want,
512
+ # we can extend this tool to return per-table rows to export individually.
513
+ return {
514
+ "status": "success",
515
+ "result": {
516
+ "markdown": md
517
+ }
518
+ }