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,602 @@
1
+ """
2
+ BigQuery Agent Implementation for AI-Parrot.
3
+
4
+ Concrete implementation of AbstractDBAgent for Google BigQuery
5
+ with support for SQL query language and BigQuery-specific features.
6
+ """
7
+
8
+ from typing import Dict, Any, List, Optional, Union
9
+ import asyncio
10
+ from datetime import datetime
11
+ from pydantic import Field
12
+ from google.cloud import bigquery as bq
13
+ from google.oauth2 import service_account
14
+ from google.cloud.exceptions import NotFound
15
+ from navconfig import config
16
+
17
+ from .abstract import (
18
+ AbstractDBAgent,
19
+ DatabaseSchema,
20
+ TableMetadata,
21
+ )
22
+ from ...tools.abstract import AbstractTool, ToolResult, AbstractToolArgsSchema
23
+
24
+
25
+ class BigQueryQueryExecutionArgs(AbstractToolArgsSchema):
26
+ """Arguments for BigQuery query execution."""
27
+ query: str = Field(description="SQL query to execute")
28
+ limit: Optional[int] = Field(
29
+ default=1000, description="Maximum number of rows to return"
30
+ )
31
+ use_legacy_sql: bool = Field(
32
+ default=False, description="Use legacy SQL instead of standard SQL"
33
+ )
34
+
35
+
36
+ class DatasetMetadata:
37
+ """Metadata for BigQuery datasets."""
38
+ def __init__(
39
+ self,
40
+ dataset_id: str,
41
+ project_id: str,
42
+ location: str,
43
+ tables: List[str],
44
+ description: str = None
45
+ ):
46
+ self.dataset_id = dataset_id
47
+ self.project_id = project_id
48
+ self.location = location
49
+ self.tables = tables
50
+ self.description = description
51
+
52
+
53
+ class BigQueryAgent(AbstractDBAgent):
54
+ """
55
+ BigQuery Agent for data warehouse introspection and SQL query generation.
56
+
57
+ Supports Google BigQuery with standard SQL and BigQuery-specific features.
58
+ """
59
+
60
+ def __init__(
61
+ self,
62
+ name: str = "BigQueryAgent",
63
+ project_id: str = None,
64
+ credentials_file: str = None,
65
+ credentials: Union[str, Dict[str, Any]] = None,
66
+ dataset: str = None,
67
+ location: str = "US",
68
+ max_sample_rows: int = 10,
69
+ **kwargs
70
+ ):
71
+ """
72
+ Initialize BigQuery Agent.
73
+
74
+ Args:
75
+ name: Agent name
76
+ project_id: Google Cloud project ID
77
+ credentials_file: Path to service account credentials JSON file
78
+ credentials: Credentials dict or connection string (overrides individual params)
79
+ dataset: Default dataset name
80
+ location: BigQuery location/region (default: US)
81
+ max_sample_rows: Maximum sample rows per table
82
+ """
83
+ self.project_id = project_id
84
+ self.credentials_file = credentials_file
85
+ self.dataset = dataset
86
+ self.location = location
87
+ self.max_sample_rows = max_sample_rows
88
+ self.client: Optional[bq.Client] = None
89
+ self.credentials_obj = None
90
+ self.datasets_cache: Dict[str, DatasetMetadata] = {}
91
+
92
+ # Get default credentials if not provided
93
+ if not credentials and not all([project_id, credentials_file]):
94
+ credentials = self._get_default_credentials()
95
+
96
+ super().__init__(
97
+ name=name,
98
+ credentials=credentials,
99
+ schema_name=dataset,
100
+ **kwargs
101
+ )
102
+
103
+ # Add BigQuery-specific tools
104
+ self._setup_bigquery_tools()
105
+
106
+ def _get_default_credentials(self) -> Dict[str, Any]:
107
+ """Get default credentials from config (similar to DatabaseQueryTool)."""
108
+ return {
109
+ 'credentials_file': config.get('GOOGLE_APPLICATION_CREDENTIALS'),
110
+ 'project_id': config.get('GOOGLE_CLOUD_PROJECT'),
111
+ }
112
+
113
+ def _setup_bigquery_tools(self):
114
+ """Setup BigQuery-specific tools."""
115
+ # Add query execution tool
116
+ query_execution_tool = BigQueryQueryExecutionTool(agent=self)
117
+ self.tool_manager.register_tool(query_execution_tool)
118
+
119
+ # Add dataset exploration tool
120
+ dataset_exploration_tool = DatasetExplorationTool(agent=self)
121
+ self.tool_manager.register_tool(dataset_exploration_tool)
122
+
123
+ async def connect_database(self) -> None:
124
+ """Connect to BigQuery using service account credentials."""
125
+ # Parse credentials
126
+ if isinstance(self.credentials, dict):
127
+ creds = self.credentials.copy()
128
+ self.project_id = creds.get('project_id', self.project_id)
129
+ self.credentials_file = creds.get('credentials_file', self.credentials_file)
130
+ self.dataset = creds.get('dataset', self.dataset)
131
+
132
+ try:
133
+ # Initialize BigQuery client
134
+ if self.credentials_file:
135
+ self.credentials_obj = service_account.Credentials.from_service_account_file(
136
+ self.credentials_file
137
+ )
138
+ if not self.project_id:
139
+ self.project_id = self.credentials_obj.project_id
140
+
141
+ self.client = bq.Client(
142
+ credentials=self.credentials_obj,
143
+ project=self.project_id
144
+ )
145
+ else:
146
+ # Use application default credentials
147
+ if not self.project_id:
148
+ raise ValueError("BigQuery project_id is required")
149
+ self.client = bq.Client(project=self.project_id)
150
+
151
+ # Test connection by listing datasets (limit to 1)
152
+ datasets = list(self.client.list_datasets(max_results=1))
153
+
154
+ self.logger.info(
155
+ f"Successfully connected to BigQuery project: {self.project_id}"
156
+ )
157
+
158
+ except Exception as e:
159
+ self.logger.error(f"Failed to connect to BigQuery: {e}")
160
+ raise
161
+
162
+ async def extract_schema_metadata(self) -> DatabaseSchema:
163
+ """Extract schema metadata from BigQuery (datasets, tables, columns)."""
164
+ if not self.client:
165
+ await self.connect_database()
166
+
167
+ try:
168
+ # Get datasets to analyze
169
+ datasets_to_analyze = []
170
+ if self.dataset:
171
+ datasets_to_analyze = [self.dataset]
172
+ else:
173
+ # List all datasets in the project
174
+ datasets = self.client.list_datasets()
175
+ datasets_to_analyze = [dataset.dataset_id for dataset in datasets]
176
+
177
+ # Extract tables from each dataset
178
+ all_tables = []
179
+ for dataset_id in datasets_to_analyze:
180
+ tables = await self._extract_tables_from_dataset(dataset_id)
181
+ all_tables.extend(tables)
182
+
183
+ schema_metadata = DatabaseSchema(
184
+ database_name=self.project_id,
185
+ database_type="bigquery",
186
+ tables=all_tables,
187
+ views=[], # Views are included in tables
188
+ functions=[],
189
+ procedures=[],
190
+ metadata={
191
+ "datasets_analyzed": datasets_to_analyze,
192
+ "total_tables": len(all_tables),
193
+ "extraction_timestamp": datetime.now().isoformat(),
194
+ "location": self.location
195
+ }
196
+ )
197
+
198
+ self.logger.info(
199
+ f"Extracted metadata for {len(all_tables)} tables from {len(datasets_to_analyze)} datasets"
200
+ )
201
+
202
+ return schema_metadata
203
+
204
+ except Exception as e:
205
+ self.logger.error(f"Failed to extract BigQuery schema metadata: {e}")
206
+ raise
207
+
208
+ async def _extract_tables_from_dataset(
209
+ self,
210
+ dataset_id: str
211
+ ) -> List[TableMetadata]:
212
+ """Extract all tables from a specific dataset."""
213
+ try:
214
+ dataset_ref = f"{self.project_id}.{dataset_id}"
215
+ tables = self.client.list_tables(dataset_ref)
216
+
217
+ table_metadata_list = []
218
+ for table in tables:
219
+ # Get full table details
220
+ table_ref = self.client.get_table(table.reference)
221
+
222
+ # Extract table metadata
223
+ table_metadata = await self._extract_table_metadata(
224
+ dataset_id, table_ref
225
+ )
226
+ table_metadata_list.append(table_metadata)
227
+
228
+ return table_metadata_list
229
+
230
+ except NotFound:
231
+ self.logger.warning(f"Dataset {dataset_id} not found")
232
+ return []
233
+ except Exception as e:
234
+ self.logger.warning(f"Could not extract tables from dataset {dataset_id}: {e}")
235
+ return []
236
+
237
+ async def _extract_table_metadata(
238
+ self,
239
+ dataset_id: str,
240
+ table: bq.Table
241
+ ) -> TableMetadata:
242
+ """Extract detailed metadata for a specific table."""
243
+ try:
244
+ # Extract columns
245
+ columns = []
246
+ for field in table.schema:
247
+ columns.append({
248
+ "name": field.name,
249
+ "type": field.field_type,
250
+ "nullable": field.mode != "REQUIRED",
251
+ "description": field.description or ""
252
+ })
253
+
254
+ # Get sample data
255
+ sample_data = []
256
+ try:
257
+ query = f"""
258
+ SELECT *
259
+ FROM `{self.project_id}.{dataset_id}.{table.table_id}`
260
+ LIMIT {self.max_sample_rows}
261
+ """
262
+ query_job = self.client.query(query)
263
+ results = query_job.result()
264
+
265
+ for row in results:
266
+ sample_data.append(dict(row))
267
+
268
+ except Exception as e:
269
+ self.logger.debug(f"Could not get sample data for {table.table_id}: {e}")
270
+
271
+ # Determine if it's a view
272
+ is_view = table.table_type == "VIEW"
273
+
274
+ return TableMetadata(
275
+ name=table.table_id,
276
+ schema=dataset_id,
277
+ columns=columns,
278
+ primary_keys=[], # BigQuery doesn't enforce primary keys
279
+ foreign_keys=[], # BigQuery doesn't enforce foreign keys
280
+ indexes=[], # BigQuery handles indexing automatically
281
+ description=table.description or f"BigQuery {'view' if is_view else 'table'} in dataset {dataset_id}",
282
+ sample_data=sample_data
283
+ )
284
+
285
+ except Exception as e:
286
+ self.logger.warning(
287
+ f"Could not extract metadata for table {table.table_id}: {e}"
288
+ )
289
+ return TableMetadata(
290
+ name=table.table_id,
291
+ schema=dataset_id,
292
+ columns=[],
293
+ primary_keys=[],
294
+ foreign_keys=[],
295
+ indexes=[],
296
+ description="",
297
+ sample_data=[]
298
+ )
299
+
300
+ async def generate_query(
301
+ self,
302
+ natural_language_query: str,
303
+ target_tables: Optional[List[str]] = None,
304
+ query_type: str = "SELECT"
305
+ ) -> Dict[str, Any]:
306
+ """Generate BigQuery SQL from natural language."""
307
+ try:
308
+ # Get schema context for the query
309
+ schema_context = []
310
+ if self.schema_metadata:
311
+ # Filter tables based on target_tables
312
+ tables_to_use = self.schema_metadata.tables
313
+ if target_tables:
314
+ tables_to_use = [
315
+ t for t in tables_to_use if t.name in target_tables
316
+ ]
317
+
318
+ for table in tables_to_use[:5]: # Limit to top 5
319
+ schema_context.append({
320
+ 'table': f"{table.schema}.{table.name}",
321
+ 'columns': [
322
+ {'name': col['name'], 'type': col['type']}
323
+ for col in table.columns
324
+ ],
325
+ 'sample_count': len(table.sample_data)
326
+ })
327
+
328
+ # Build SQL query generation prompt
329
+ prompt = self._build_sql_query_prompt(
330
+ natural_language_query=natural_language_query,
331
+ schema_context=schema_context
332
+ )
333
+
334
+ # Generate query using LLM
335
+ async with self._llm as client:
336
+ response = await client.ask(
337
+ prompt=prompt,
338
+ model=self._llm_model,
339
+ temperature=0.1
340
+ )
341
+
342
+ # Extract SQL query from response
343
+ sql_query = self._extract_sql_from_response(response.output or response.response)
344
+
345
+ result = {
346
+ "query": sql_query,
347
+ "query_type": "sql",
348
+ "tables_used": target_tables or [],
349
+ "schema_context_used": len(schema_context),
350
+ "natural_language_input": natural_language_query
351
+ }
352
+
353
+ return result
354
+
355
+ except Exception as e:
356
+ self.logger.error(f"Failed to generate SQL query: {e}")
357
+ raise
358
+
359
+ def _build_sql_query_prompt(
360
+ self,
361
+ natural_language_query: str,
362
+ schema_context: List[Dict[str, Any]]
363
+ ) -> str:
364
+ """Build prompt for SQL query generation."""
365
+ prompt = f"""
366
+ You are an expert BigQuery SQL developer.
367
+ Generate a BigQuery SQL query based on the natural language request and the provided schema information.
368
+
369
+ Natural Language Request: {natural_language_query}
370
+
371
+ Available Tables and Schema:
372
+ """
373
+ for i, context in enumerate(schema_context, 1):
374
+ prompt += f"\n{i}. Table: {context['table']}\n"
375
+ prompt += " Columns:\n"
376
+ for col in context['columns'][:10]: # Limit columns shown
377
+ prompt += f" - {col['name']} ({col['type']})\n"
378
+
379
+ prompt += f"""
380
+
381
+ BigQuery SQL Guidelines:
382
+ 1. Use standard SQL syntax (not legacy SQL)
383
+ 2. Fully qualify table names: `project.dataset.table`
384
+ 3. Use backticks for table/column names with special characters
385
+ 4. Common BigQuery functions: STRING_AGG, ARRAY_AGG, APPROX_COUNT_DISTINCT
386
+ 5. Use STRUCT and ARRAY types when appropriate
387
+ 6. For date/time: CURRENT_TIMESTAMP(), DATE_SUB(), TIMESTAMP_DIFF()
388
+ 7. Use WITH clauses (CTEs) for complex queries
389
+ 8. Return only the SQL query without explanations or markdown formatting
390
+
391
+ Project ID: {self.project_id}
392
+
393
+ SQL Query:"""
394
+
395
+ return prompt
396
+
397
+ def _extract_sql_from_response(self, response_text: str) -> str:
398
+ """Extract SQL query from LLM response."""
399
+ # Remove markdown code blocks if present
400
+ if "```" in response_text:
401
+ lines = response_text.split('\n')
402
+ sql_lines = []
403
+ in_code_block = False
404
+
405
+ for line in lines:
406
+ if line.strip().startswith("```"):
407
+ in_code_block = not in_code_block
408
+ continue
409
+ elif in_code_block:
410
+ sql_lines.append(line)
411
+
412
+ return '\n'.join(sql_lines).strip()
413
+ else:
414
+ return response_text.strip()
415
+
416
+ async def execute_query(
417
+ self,
418
+ query: str,
419
+ limit: Optional[int] = 1000
420
+ ) -> Dict[str, Any]:
421
+ """Execute SQL query against BigQuery."""
422
+ try:
423
+ if not self.client:
424
+ await self.connect_database()
425
+
426
+ # Add limit if not present
427
+ query_upper = query.upper().strip()
428
+ if limit and 'LIMIT' not in query_upper:
429
+ query = f"{query.rstrip(';')} LIMIT {limit}"
430
+
431
+ # Configure query job
432
+ job_config = bq.QueryJobConfig()
433
+ job_config.use_legacy_sql = False
434
+
435
+ # Execute query
436
+ query_job = self.client.query(query, job_config=job_config)
437
+ results = query_job.result()
438
+
439
+ # Process results
440
+ rows = []
441
+ columns = []
442
+
443
+ if results.schema:
444
+ columns = [field.name for field in results.schema]
445
+
446
+ for row in results:
447
+ rows.append(dict(row))
448
+
449
+ return {
450
+ "success": True,
451
+ "data": rows,
452
+ "columns": columns,
453
+ "record_count": len(rows),
454
+ "query": query,
455
+ "total_bytes_processed": query_job.total_bytes_processed,
456
+ "total_bytes_billed": query_job.total_bytes_billed
457
+ }
458
+
459
+ except Exception as e:
460
+ self.logger.error(f"BigQuery query execution failed: {e}")
461
+ return {
462
+ "success": False,
463
+ "error": str(e),
464
+ "query": query
465
+ }
466
+
467
+ async def close(self):
468
+ """Close BigQuery client connection."""
469
+ if self.client:
470
+ self.client.close()
471
+
472
+
473
+ class BigQueryQueryExecutionTool(AbstractTool):
474
+ """Tool for executing SQL queries against BigQuery."""
475
+
476
+ name = "execute_bigquery_query"
477
+ description = "Execute SQL queries against Google BigQuery"
478
+ args_schema = BigQueryQueryExecutionArgs
479
+
480
+ def __init__(self, agent: BigQueryAgent, **kwargs):
481
+ super().__init__(**kwargs)
482
+ self.agent = agent
483
+
484
+ async def _execute(
485
+ self,
486
+ query: str,
487
+ limit: Optional[int] = 1000,
488
+ use_legacy_sql: bool = False
489
+ ) -> ToolResult:
490
+ """Execute BigQuery query."""
491
+ try:
492
+ result = await self.agent.execute_query(query, limit)
493
+
494
+ return ToolResult(
495
+ status="success" if result["success"] else "error",
496
+ result=result,
497
+ error=result.get("error"),
498
+ metadata={
499
+ "query": query,
500
+ "limit": limit,
501
+ "bytes_processed": result.get("total_bytes_processed"),
502
+ "bytes_billed": result.get("total_bytes_billed")
503
+ }
504
+ )
505
+
506
+ except Exception as e:
507
+ return ToolResult(
508
+ status="error",
509
+ result=None,
510
+ error=str(e),
511
+ metadata={"query": query}
512
+ )
513
+
514
+
515
+ class DatasetExplorationTool(AbstractTool):
516
+ """Tool for exploring BigQuery datasets and tables."""
517
+
518
+ name = "explore_bigquery_datasets"
519
+ description = "Explore available datasets and tables in BigQuery"
520
+
521
+ class ExplorationArgs(AbstractToolArgsSchema):
522
+ """Exploration arguments schema."""
523
+ dataset: Optional[str] = Field(
524
+ default=None, description="Specific dataset to explore"
525
+ )
526
+ table: Optional[str] = Field(
527
+ default=None, description="Specific table to explore"
528
+ )
529
+ show_sample_data: bool = Field(
530
+ default=True, description="Include sample data in results"
531
+ )
532
+
533
+ args_schema = ExplorationArgs
534
+
535
+ def __init__(self, agent: BigQueryAgent, **kwargs):
536
+ super().__init__(**kwargs)
537
+ self.agent = agent
538
+
539
+ async def _execute(
540
+ self,
541
+ dataset: Optional[str] = None,
542
+ table: Optional[str] = None,
543
+ show_sample_data: bool = True
544
+ ) -> ToolResult:
545
+ """Explore datasets/tables in BigQuery."""
546
+ try:
547
+ if not self.agent.schema_metadata:
548
+ await self.agent.extract_schema_metadata()
549
+
550
+ exploration_result = {
551
+ "datasets": [],
552
+ "tables": [],
553
+ "total_tables": 0
554
+ }
555
+
556
+ # Filter tables
557
+ tables_to_explore = self.agent.schema_metadata.tables
558
+ if dataset:
559
+ tables_to_explore = [t for t in tables_to_explore if t.schema == dataset]
560
+ if table:
561
+ tables_to_explore = [t for t in tables_to_explore if t.name == table]
562
+
563
+ # Get unique datasets
564
+ datasets = list(set(t.schema for t in tables_to_explore))
565
+ exploration_result["datasets"] = datasets
566
+
567
+ # Build table information
568
+ for tbl in tables_to_explore:
569
+ table_info = {
570
+ "name": tbl.name,
571
+ "dataset": tbl.schema,
572
+ "full_name": f"{tbl.schema}.{tbl.name}",
573
+ "columns": [
574
+ {'name': col['name'], 'type': col['type']}
575
+ for col in tbl.columns
576
+ ],
577
+ "description": tbl.description
578
+ }
579
+
580
+ if show_sample_data and tbl.sample_data:
581
+ table_info["sample_data"] = tbl.sample_data[:3]
582
+
583
+ exploration_result["tables"].append(table_info)
584
+
585
+ exploration_result["total_tables"] = len(exploration_result["tables"])
586
+
587
+ return ToolResult(
588
+ status="success",
589
+ result=exploration_result,
590
+ metadata={
591
+ "project": self.agent.project_id,
592
+ "dataset_filter": dataset,
593
+ "table_filter": table
594
+ }
595
+ )
596
+
597
+ except Exception as e:
598
+ return ToolResult(
599
+ status="error",
600
+ result=None,
601
+ error=str(e)
602
+ )
@@ -0,0 +1,85 @@
1
+ from typing import Dict, Any, List, Optional, Union
2
+ import hashlib
3
+ from redis.asyncio import Redis
4
+ from datamodel.parsers.json import json_encoder, json_decoder # pylint: disable=E0611 # noqa
5
+ from querysource.conf import CACHE_URL
6
+
7
+
8
+ class SchemaCache:
9
+ """Redis-based LRU cache for schema metadata."""
10
+
11
+ def __init__(self, redis_url: str = None, key_prefix: str = "schema_cache", ttl: int = 3600):
12
+ self.redis_url = redis_url or CACHE_URL
13
+ self.key_prefix = key_prefix
14
+ self.ttl = ttl # Time to live in seconds
15
+ self._redis = None
16
+
17
+ async def _get_redis(self) -> Redis:
18
+ """Get or create Redis connection."""
19
+ if self._redis is None:
20
+ self._redis = Redis.from_url(
21
+ self.redis_url,
22
+ decode_responses=True,
23
+ encoding="utf-8",
24
+ socket_connect_timeout=5,
25
+ socket_timeout=5,
26
+ retry_on_timeout=True
27
+ )
28
+ return self._redis
29
+
30
+ def _make_cache_key(self, search_term: str, search_type: str = "all", limit: int = 10) -> str:
31
+ """Create a cache key for the search parameters."""
32
+ # Create a hash of the parameters for consistent key generation
33
+ params = f"{search_term}:{search_type}:{limit}"
34
+ hash_key = hashlib.md5(params.encode()).hexdigest()
35
+ return f"{self.key_prefix}:search:{hash_key}"
36
+
37
+ async def get(
38
+ self,
39
+ search_term: str,
40
+ search_type: str = "all",
41
+ limit: int = 10
42
+ ) -> Optional[List[Dict[str, Any]]]:
43
+ """Get cached search results."""
44
+ try:
45
+ redis = await self._get_redis()
46
+ cache_key = self._make_cache_key(search_term, search_type, limit)
47
+ data = await redis.get(cache_key)
48
+ if data:
49
+ return json_decoder(data)
50
+ except Exception as e:
51
+ # Log error but don't fail the operation
52
+ print(f"Cache get error: {e}")
53
+ return None
54
+
55
+ async def set(
56
+ self,
57
+ search_term: str,
58
+ search_type: str,
59
+ limit: int,
60
+ results: List[Dict[str, Any]]
61
+ ) -> None:
62
+ """Set cached search results with TTL."""
63
+ try:
64
+ redis = await self._get_redis()
65
+ cache_key = self._make_cache_key(search_term, search_type, limit)
66
+ data = json_encoder(results)
67
+ await redis.setex(cache_key, self.ttl, data)
68
+ except Exception as e:
69
+ # Log error but don't fail the operation
70
+ print(f"Cache set error: {e}")
71
+
72
+ async def invalidate_all(self) -> None:
73
+ """Invalidate all cache entries."""
74
+ try:
75
+ redis = await self._get_redis()
76
+ keys = await redis.keys(f"{self.key_prefix}:*")
77
+ if keys:
78
+ await redis.delete(*keys)
79
+ except Exception as e:
80
+ print(f"Cache invalidation error: {e}")
81
+
82
+ async def close(self) -> None:
83
+ """Close Redis connection."""
84
+ if self._redis:
85
+ await self._redis.close()