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,286 @@
1
+ from typing import Any, Dict, List, Optional
2
+ from abc import ABC, abstractmethod
3
+ from pydantic import Field
4
+ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
5
+ from sqlalchemy.orm import sessionmaker
6
+ from navconfig.logging import logging
7
+ from ..abstract import (
8
+ AbstractTool,
9
+ ToolResult,
10
+ AbstractToolArgsSchema
11
+ )
12
+ from ...stores.abstract import AbstractStore
13
+ from ...conf import asyncpg_sqlalchemy_url
14
+ from .models import TableMetadata
15
+ from .cache import SchemaMetadataCache
16
+
17
+
18
+ class SchemaSearchArgs(AbstractToolArgsSchema):
19
+ """Arguments for schema search tool."""
20
+ search_term: str = Field(
21
+ description="Term to search for in table names, column names, or descriptions"
22
+ )
23
+ schema_name: Optional[str] = Field(
24
+ default=None,
25
+ description="Schema name to search in"
26
+ )
27
+ table_name: Optional[str] = Field(
28
+ default=None,
29
+ description="Table name to search in"
30
+ )
31
+ search_type: str = Field(
32
+ default="all",
33
+ description="Type of search: 'tables', 'columns', 'descriptions', or 'all'"
34
+ )
35
+ limit: int = Field(
36
+ default=5,
37
+ description="Maximum number of results to return"
38
+ )
39
+
40
+
41
+ class AbstractSchemaManagerTool(AbstractTool, ABC):
42
+ """
43
+ Abstract base for database-specific schema management tools.
44
+
45
+ Handles all schema-related operations:
46
+ - Schema analysis and metadata extraction
47
+ - Schema search and discovery
48
+ - Metadata caching and retrieval
49
+ """
50
+
51
+ name = "SchemaManagerTool"
52
+ description = "Comprehensive schema management for database operations"
53
+ args_schema = SchemaSearchArgs
54
+
55
+ def __init__(
56
+ self,
57
+ allowed_schemas: List[str],
58
+ engine: AsyncEngine = None,
59
+ metadata_cache: SchemaMetadataCache = None,
60
+ vector_store: Optional[AbstractStore] = None,
61
+ dsn: Optional[str] = None,
62
+ database_type: str = "postgresql",
63
+ session_maker: Optional[sessionmaker] = None,
64
+ **kwargs
65
+ ):
66
+ super().__init__(**kwargs)
67
+ self.dsn = dsn or asyncpg_sqlalchemy_url
68
+ self.database_type = database_type
69
+ self.allowed_schemas = allowed_schemas
70
+ # Database components
71
+ self.engine: Optional[AsyncEngine] = engine or self._get_engine(
72
+ dsn=self.dsn,
73
+ search_path=",".join(allowed_schemas)
74
+ )
75
+ # Schema-aware components
76
+ self.metadata_cache = metadata_cache or SchemaMetadataCache(
77
+ vector_store=vector_store, # Optional - can be None
78
+ lru_maxsize=500, # Large cache for many tables
79
+ lru_ttl=1800 # 30 minutes
80
+ )
81
+ # Vector Store:
82
+ self.knowledge_store = vector_store
83
+
84
+ if session_maker:
85
+ self.session_maker = session_maker
86
+ else:
87
+ self.session_maker = sessionmaker(
88
+ self.engine,
89
+ class_=AsyncSession,
90
+ expire_on_commit=False
91
+ )
92
+
93
+ self.logger = logging.getLogger(f"{self.__class__.__name__}")
94
+ self.logger.debug(
95
+ f"Initialized with {len(allowed_schemas)} schemas: {allowed_schemas}"
96
+ )
97
+
98
+ def _get_engine(self, dsn: str, search_path: str) -> AsyncEngine:
99
+ """Create and return an AsyncEngine for the given DSN."""
100
+ from sqlalchemy.ext.asyncio import create_async_engine
101
+ return create_async_engine(
102
+ dsn,
103
+ echo=False,
104
+ pool_pre_ping=True,
105
+ pool_recycle=3600,
106
+ # Multi-schema search path
107
+ connect_args={
108
+ "server_settings": {
109
+ "search_path": search_path
110
+ }
111
+ }
112
+ )
113
+
114
+ async def _execute(
115
+ self,
116
+ search_term: str,
117
+ search_type: str = "all",
118
+ limit: int = 10
119
+ ) -> ToolResult:
120
+ """Execute schema search with the provided parameters."""
121
+ try:
122
+ raw_results = await self.search_schema(search_term, search_type, limit)
123
+
124
+ formatted_results = []
125
+ for table in raw_results:
126
+ formatted_result = await self._format_table_result(table, search_term, search_type)
127
+ if formatted_result:
128
+ formatted_results.append(formatted_result)
129
+
130
+ return ToolResult(
131
+ status="success",
132
+ result=formatted_results,
133
+ metadata={
134
+ "search_term": search_term,
135
+ "search_type": search_type,
136
+ "results_count": len(formatted_results),
137
+ "searched_schemas": self.allowed_schemas
138
+ }
139
+ )
140
+ except Exception as e:
141
+ self.logger.error(f"Schema search failed: {e}")
142
+ return ToolResult(
143
+ status="error",
144
+ result=None,
145
+ error=str(e),
146
+ metadata={"search_term": search_term}
147
+ )
148
+
149
+ async def analyze_all_schemas(self) -> Dict[str, int]:
150
+ """
151
+ Analyze all allowed schemas and populate metadata cache.
152
+ Returns dict of schema_name -> table_count.
153
+ """
154
+ self.logger.info(f"Analyzing schemas: {self.allowed_schemas}")
155
+
156
+ results = {}
157
+ total_tables = 0
158
+
159
+ for schema_name in self.allowed_schemas:
160
+ try:
161
+ table_count = await self.analyze_schema(schema_name)
162
+ results[schema_name] = table_count
163
+ total_tables += table_count
164
+ self.logger.info(f"Schema '{schema_name}': {table_count} tables/views analyzed")
165
+ except Exception as e:
166
+ self.logger.warning(f"Failed to analyze schema '{schema_name}': {e}")
167
+ results[schema_name] = 0
168
+ continue
169
+
170
+ self.logger.info(
171
+ f"Analysis completed. Total: {total_tables} tables across {len(self.allowed_schemas)} schemas"
172
+ )
173
+ return results
174
+
175
+ @abstractmethod
176
+ async def analyze_schema(self, schema_name: str) -> int:
177
+ """
178
+ Analyze individual schema and return table count.
179
+ Must be implemented by database-specific subclasses.
180
+ """
181
+ pass
182
+
183
+ @abstractmethod
184
+ async def analyze_table(
185
+ self,
186
+ session: AsyncSession,
187
+ schema_name: str,
188
+ table_name: str,
189
+ table_type: str,
190
+ comment: Optional[str]
191
+ ) -> TableMetadata:
192
+ """
193
+ Analyze individual table metadata.
194
+ Must be implemented by database-specific subclasses.
195
+ """
196
+ pass
197
+
198
+ async def search_schema(
199
+ self,
200
+ search_term: str,
201
+ search_type: str = "all",
202
+ limit: int = 10
203
+ ) -> List[TableMetadata]:
204
+ """Search database schema - returns raw TableMetadata for agent use."""
205
+ self.logger.debug(
206
+ f"🔍 SCHEMA SEARCH: '{search_term}' (type: {search_type}, limit: {limit})")
207
+
208
+ tables = await self.metadata_cache.search_similar_tables(
209
+ schema_names=self.allowed_schemas,
210
+ query=search_term,
211
+ limit=limit
212
+ )
213
+
214
+ self.logger.info(f"✅ SEARCH COMPLETE: Found {len(tables)} results")
215
+ return tables
216
+
217
+ async def _format_table_result(
218
+ self,
219
+ table: TableMetadata,
220
+ search_term: str,
221
+ search_type: str
222
+ ) -> Optional[Dict[str, Any]]:
223
+ """Format a table metadata object into a search result."""
224
+
225
+ # Always return since cache already did filtering
226
+ return {
227
+ "type": "table",
228
+ "schema": table.schema,
229
+ "tablename": table.tablename,
230
+ "full_name": table.full_name,
231
+ "table_type": table.table_type,
232
+ "description": table.comment,
233
+ "columns": [
234
+ {
235
+ "name": col.get('name'),
236
+ "type": col.get('type'),
237
+ "nullable": col.get('nullable', True),
238
+ "description": col.get('description')
239
+ }
240
+ for col in table.columns
241
+ ],
242
+ "row_count": table.row_count,
243
+ "sample_data": table.sample_data[:3] if table.sample_data else [],
244
+ "search_term": search_term,
245
+ "search_type": search_type
246
+ }
247
+
248
+ async def get_table_details(
249
+ self,
250
+ schema: str,
251
+ tablename: str
252
+ ) -> Optional[TableMetadata]:
253
+ """Get detailed metadata for a specific table."""
254
+ if schema not in self.allowed_schemas:
255
+ self.logger.warning(
256
+ f"Schema '{schema}' not in allowed schemas: {self.allowed_schemas}"
257
+ )
258
+ return None
259
+
260
+ try:
261
+ return await self.metadata_cache.get_table_metadata(schema, tablename)
262
+ except Exception as e:
263
+ self.logger.error(f"Failed to get table details for {schema}.{tablename}: {e}")
264
+ return None
265
+
266
+ async def get_schema_overview(self, schema_name: str) -> Optional[Dict[str, Any]]:
267
+ """Get overview of a specific schema."""
268
+ if schema_name not in self.allowed_schemas:
269
+ return None
270
+
271
+ if schema_meta := self.metadata_cache.get_schema_overview(schema_name):
272
+ return {
273
+ "schema": schema_meta.schema,
274
+ "database_name": schema_meta.database_name,
275
+ "table_count": schema_meta.table_count,
276
+ "view_count": schema_meta.view_count,
277
+ "total_rows": schema_meta.total_rows,
278
+ "last_analyzed": schema_meta.last_analyzed.isoformat() if schema_meta.last_analyzed else None,
279
+ "tables": list(schema_meta.tables.keys()),
280
+ "views": list(schema_meta.views.keys())
281
+ }
282
+ return None
283
+
284
+ def get_allowed_schemas(self) -> List[str]:
285
+ """Get the list of schemas this tool can search."""
286
+ return self.allowed_schemas.copy()
@@ -0,0 +1,115 @@
1
+ from typing import Optional, List
2
+ from sqlalchemy import text
3
+ from sqlalchemy.ext.asyncio import AsyncSession
4
+ from datetime import datetime
5
+ from .abstract import AbstractSchemaManagerTool
6
+ from .models import TableMetadata
7
+
8
+ class BQSchemaSearchTool(AbstractSchemaManagerTool):
9
+ """BigQuery-specific schema manager tool."""
10
+
11
+ name = "BQSchemaSearchTool"
12
+ description = "Schema management for BigQuery databases"
13
+
14
+ async def analyze_schema(self, schema_name: str) -> int:
15
+ """Analyze individual BigQuery schema (dataset) and return table count."""
16
+ async with self.session_maker() as session:
17
+ try:
18
+ # Query INFORMATION_SCHEMA.TABLES for the dataset
19
+ query_str = f"SELECT table_name, table_type FROM `{schema_name}.INFORMATION_SCHEMA.TABLES` WHERE table_type IN ('BASE TABLE', 'VIEW')"
20
+
21
+ result = await session.execute(text(query_str))
22
+ tables_data = result.fetchall()
23
+
24
+ for table_row in tables_data:
25
+ table_name = table_row.table_name
26
+ table_type = table_row.table_type
27
+ comment = None
28
+
29
+ try:
30
+ table_metadata = await self.analyze_table(
31
+ session, schema_name, table_name, table_type, comment
32
+ )
33
+ await self.metadata_cache.store_table_metadata(table_metadata)
34
+ except Exception as e:
35
+ self.logger.warning(
36
+ f"Failed to analyze table {schema_name}.{table_name}: {e}"
37
+ )
38
+
39
+ return len(tables_data)
40
+ except Exception as e:
41
+ self.logger.error(f"Error accessing schema {schema_name}: {e}")
42
+ return 0
43
+
44
+ async def analyze_table(
45
+ self,
46
+ session: AsyncSession,
47
+ schema_name: str,
48
+ table_name: str,
49
+ table_type: str,
50
+ comment: Optional[str]
51
+ ) -> TableMetadata:
52
+ """Analyze individual BigQuery table metadata."""
53
+
54
+ # Get columns
55
+ columns_query = f"""
56
+ SELECT
57
+ column_name,
58
+ data_type,
59
+ is_nullable,
60
+ NULL as column_default,
61
+ NULL as character_maximum_length,
62
+ NULL as comment
63
+ FROM `{schema_name}.INFORMATION_SCHEMA.COLUMNS`
64
+ WHERE table_name = :table_name
65
+ ORDER BY ordinal_position
66
+ """
67
+
68
+ result = await session.execute(
69
+ text(columns_query),
70
+ {"table_name": table_name}
71
+ )
72
+
73
+ columns = []
74
+ for col_row in result.fetchall():
75
+ columns.append({
76
+ "name": col_row.column_name,
77
+ "type": col_row.data_type,
78
+ "nullable": col_row.is_nullable == "YES",
79
+ "default": col_row.column_default,
80
+ "max_length": col_row.character_maximum_length,
81
+ "comment": col_row.comment
82
+ })
83
+
84
+ # Primary keys are generally not as critical/available in standard BQ metadata queries
85
+ # in the same way as Postgres, so leaving empty for now.
86
+ primary_keys = []
87
+
88
+ row_count = None
89
+
90
+ sample_data = []
91
+ if table_type == 'BASE TABLE':
92
+ try:
93
+ sample_query = f'SELECT * FROM `{schema_name}.{table_name}` LIMIT 3'
94
+ sample_result = await session.execute(text(sample_query))
95
+ rows = sample_result.fetchall()
96
+ if rows:
97
+ column_names = list(sample_result.keys())
98
+ sample_data = [dict(zip(column_names, row)) for row in rows]
99
+ except Exception as e:
100
+ pass
101
+
102
+ return TableMetadata(
103
+ schema=schema_name,
104
+ tablename=table_name,
105
+ table_type=table_type,
106
+ full_name=f'`{schema_name}.{table_name}`',
107
+ comment=comment,
108
+ columns=columns,
109
+ primary_keys=primary_keys,
110
+ foreign_keys=[],
111
+ indexes=[],
112
+ row_count=row_count,
113
+ sample_data=sample_data,
114
+ last_accessed=datetime.now()
115
+ )
@@ -0,0 +1,284 @@
1
+ # ============================================================================
2
+ # SCHEMA-AWARE METADATA CACHE
3
+ # ============================================================================
4
+ from typing import Dict, List, Optional
5
+ import re
6
+ from cachetools import TTLCache
7
+ from navconfig.logging import logging
8
+ from .models import SchemaMetadata, TableMetadata
9
+ from ...stores.abstract import AbstractStore
10
+
11
+ class SchemaMetadataCache:
12
+ """Two-tier caching: LRU (hot data) + Optional Vector Store (cold/searchable data)."""
13
+
14
+ def __init__(
15
+ self,
16
+ vector_store: Optional[AbstractStore] = None,
17
+ lru_maxsize: int = 500, # Increased for large schema count
18
+ lru_ttl: int = 1800 # 30 minutes
19
+ ):
20
+ # Tier 1: LRU Cache for frequently accessed metadata
21
+ self.hot_cache = TTLCache(maxsize=lru_maxsize, ttl=lru_ttl)
22
+
23
+ # Tier 2: Optional Vector Store for similarity search and persistence
24
+ self.vector_store = vector_store
25
+ self.vector_enabled = vector_store is not None
26
+
27
+ # Schema-level caches
28
+ self.schema_cache: Dict[str, SchemaMetadata] = {}
29
+ self.table_access_stats: Dict[str, int] = {}
30
+
31
+ self.logger = logging.getLogger("Parrot.SchemaMetadataCache")
32
+
33
+ if not self.vector_enabled:
34
+ print("Vector store not provided - using LRU cache only")
35
+
36
+ def _table_cache_key(self, schema_name: str, table_name: str) -> str:
37
+ """Generate cache key for table metadata."""
38
+ return f"table:{schema_name}:{table_name}"
39
+
40
+ def _schema_cache_key(self, schema_name: str) -> str:
41
+ """Generate cache key for schema metadata."""
42
+ return f"schema:{schema_name}"
43
+
44
+ async def get_table_metadata(
45
+ self,
46
+ schema_name: str,
47
+ table_name: str
48
+ ) -> Optional[TableMetadata]:
49
+ """Get table metadata with access tracking."""
50
+ cache_key = self._table_cache_key(schema_name, table_name)
51
+
52
+ # Check hot cache first
53
+ if cache_key in self.hot_cache:
54
+ metadata = self.hot_cache[cache_key]
55
+ self._track_access(cache_key)
56
+ return metadata
57
+
58
+ # Check schema cache
59
+ if schema_name in self.schema_cache:
60
+ schema_meta = self.schema_cache[schema_name]
61
+ all_objects = schema_meta.get_all_objects()
62
+ if table_name in all_objects:
63
+ metadata = all_objects[table_name]
64
+ # Promote to hot cache
65
+ self.hot_cache[cache_key] = metadata
66
+ self._track_access(cache_key)
67
+ return metadata
68
+
69
+ # Check vector store only if enabled
70
+ if self.vector_enabled:
71
+ search_results = await self._search_vector_store(schema_name, table_name)
72
+ if search_results:
73
+ # Store in hot cache
74
+ self.hot_cache[cache_key] = search_results
75
+ self._track_access(cache_key)
76
+ return search_results
77
+
78
+ return None
79
+
80
+ async def store_table_metadata(self, metadata: TableMetadata):
81
+ """Store table metadata in available cache tiers."""
82
+ cache_key = self._table_cache_key(metadata.schema, metadata.tablename)
83
+
84
+ # Store in hot cache
85
+ self.hot_cache[cache_key] = metadata
86
+
87
+ # Update schema cache
88
+ if metadata.schema not in self.schema_cache:
89
+ self.schema_cache[metadata.schema] = SchemaMetadata(
90
+ schema=metadata.schema,
91
+ database_name="navigator", # Could be dynamic
92
+ table_count=0,
93
+ view_count=0
94
+ )
95
+
96
+ schema_meta = self.schema_cache[metadata.schema]
97
+ if metadata.table_type == 'BASE TABLE':
98
+ schema_meta.tables[metadata.tablename] = metadata
99
+ else:
100
+ schema_meta.views[metadata.tablename] = metadata
101
+
102
+ # Store in vector store only if enabled
103
+ if self.vector_enabled:
104
+ await self._store_in_vector_store(metadata)
105
+
106
+ async def search_similar_tables(
107
+ self,
108
+ schema_names: List[str],
109
+ query: str,
110
+ limit: int = 5
111
+ ) -> List[TableMetadata]:
112
+ """Search for similar tables within allowed schemas."""
113
+ if not self.vector_enabled:
114
+ # Fallback: search in LRU cache and schema cache
115
+ return self._search_cache_only(schema_names, query, limit)
116
+
117
+ # Search with multi-schema filter
118
+ search_query = f"schemas:{','.join(schema_names)} {query}"
119
+ try:
120
+ results = await self.vector_store.similarity_search(
121
+ search_query,
122
+ k=limit,
123
+ filter={"schema_name": {"$in": schema_names}} # Multi-schema filter
124
+ )
125
+
126
+ # Convert results back to TableMetadata
127
+ return await self._convert_vector_results(results)
128
+ except Exception:
129
+ # Fallback to cache-only search
130
+ return self._search_cache_only(schema_names, query, limit)
131
+
132
+ def _search_cache_only(
133
+ self,
134
+ schema_names: List[str],
135
+ query: str,
136
+ limit: int
137
+ ) -> List[TableMetadata]:
138
+ """Fallback search using only cache when vector store unavailable."""
139
+ results = []
140
+ query_lower = query.lower()
141
+ keywords = self._extract_search_keywords(query_lower)
142
+
143
+ self.logger.notice(
144
+ f"🔍 SEARCH: Extracted keywords from '{query}': {keywords}"
145
+ )
146
+
147
+ # Search through schema caches
148
+ for schema_name in schema_names:
149
+ if schema_name in self.schema_cache:
150
+ schema_meta = self.schema_cache[schema_name]
151
+ all_objects = schema_meta.get_all_objects()
152
+
153
+ self.logger.debug(
154
+ f"🔍 SEARCHING SCHEMA '{schema_name}': {len(all_objects)} tables available"
155
+ )
156
+
157
+ for table_name, table_meta in all_objects.items():
158
+ score = self._calculate_relevance_score(table_name, table_meta, keywords)
159
+
160
+ if score > 0:
161
+ self.logger.debug(
162
+ f"🔍 MATCH: {table_name} scored {score}"
163
+ )
164
+ # IMPORTANT: Return the actual TableMetadata object, not a tuple
165
+ results.append(table_meta) # FIX: was (table_meta_copy, score)
166
+
167
+ if len(results) >= limit:
168
+ break
169
+
170
+ if len(results) >= limit:
171
+ break
172
+
173
+ # FIX: Sort by a different method since we're not storing scores anymore
174
+ # For now, just return in order found (cache already does some relevance filtering)
175
+ final_results = results[:limit]
176
+
177
+ self.logger.info(f"🔍 SEARCH: Found {len(final_results)} results")
178
+
179
+ # DEBUG: Log what we're returning
180
+ for i, table in enumerate(final_results):
181
+ self.logger.debug(f" Result {i+1}: {table.schema}.{table.tablename}")
182
+
183
+ return final_results
184
+
185
+ def _extract_search_keywords(self, query: str) -> List[str]:
186
+ """Extract meaningful keywords from a natural language query."""
187
+ # Convert to lowercase and remove common stop words
188
+ stop_words = {
189
+ 'get', 'show', 'find', 'list', 'select', 'by', 'from', 'the', 'a', 'an',
190
+ 'and', 'or', 'of', 'to', 'in', 'on', 'at', 'for', 'with', 'top', 'all'
191
+ }
192
+
193
+ # Split on non-alphanumeric characters and filter
194
+ words = re.findall(r'\b[a-zA-Z]+\b', query.lower())
195
+ return [word for word in words if word not in stop_words and len(word) > 2]
196
+
197
+ def _calculate_relevance_score(
198
+ self,
199
+ table_name: str,
200
+ table_meta: TableMetadata,
201
+ keywords: List[str]
202
+ ) -> float:
203
+ """Calculate relevance score for a table based on keywords."""
204
+ score = 0.0
205
+
206
+ table_name_lower = table_name.lower()
207
+ column_names = [col['name'].lower() for col in table_meta.columns]
208
+
209
+ for keyword in keywords:
210
+ keyword_lower = keyword.lower()
211
+
212
+ if keyword_lower == table_name_lower:
213
+ score += 10.0
214
+ self.logger.debug(f"Exact table match: '{keyword}' == '{table_name}'")
215
+
216
+ elif keyword_lower in table_name_lower:
217
+ score += 5.0
218
+ self.logger.debug(f"Partial table match: '{keyword}' in '{table_name}'")
219
+
220
+ elif keyword_lower in column_names:
221
+ score += 8.0
222
+ self.logger.debug(f"Column match: '{keyword}' found in columns")
223
+
224
+ elif any(keyword_lower in col_name for col_name in column_names):
225
+ score += 3.0
226
+ self.logger.debug(f"Partial column match: '{keyword}' partially matches column")
227
+
228
+ elif table_meta.comment and keyword_lower in table_meta.comment.lower():
229
+ score += 2.0
230
+ self.logger.debug(f"Comment match: '{keyword}' in table comment")
231
+
232
+ return score
233
+
234
+ def get_schema_overview(self, schema_name: str) -> Optional[SchemaMetadata]:
235
+ """Get complete schema overview."""
236
+ return self.schema_cache.get(schema_name)
237
+
238
+ def get_hot_tables(self, schema_names: List[str], limit: int = 10) -> List[tuple[str, str, int]]:
239
+ """Get most frequently accessed tables across allowed schemas."""
240
+ schema_access = []
241
+
242
+ for schema_name in schema_names:
243
+ schema_prefix = f"table:{schema_name}:"
244
+ for key, count in self.table_access_stats.items():
245
+ if key.startswith(schema_prefix):
246
+ table_name = key.replace(schema_prefix, "")
247
+ schema_access.append((schema_name, table_name, count))
248
+
249
+ return sorted(schema_access, key=lambda x: x[2], reverse=True)[:limit]
250
+
251
+ def _track_access(self, cache_key: str):
252
+ """Track table access for hot table identification."""
253
+ self.table_access_stats[cache_key] = self.table_access_stats.get(cache_key, 0) + 1
254
+
255
+ async def _search_vector_store(self, schema_name: str, table_name: str) -> Optional[TableMetadata]:
256
+ """Search vector store for specific table."""
257
+ # Implementation depends on your vector store
258
+ if not self.vector_enabled:
259
+ return None
260
+ return None
261
+
262
+ async def _store_in_vector_store(self, metadata: TableMetadata):
263
+ """Store metadata in vector store."""
264
+ if not self.vector_enabled:
265
+ return
266
+ try:
267
+ document = {
268
+ "content": metadata.to_yaml_context(),
269
+ "metadata": {
270
+ "type": "table_metadata",
271
+ "schema": metadata.schema,
272
+ "tablename": metadata.tablename,
273
+ "table_type": metadata.table_type,
274
+ "full_name": metadata.full_name
275
+ }
276
+ }
277
+ await self.vector_store.add_documents([document])
278
+ except Exception:
279
+ pass # Silent failure for vector store issues
280
+
281
+ async def _convert_vector_results(self, results) -> List[TableMetadata]:
282
+ """Convert vector store results to TableMetadata objects."""
283
+ # Implementation depends on your vector store format
284
+ return []