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,644 @@
1
+ """
2
+ Abstract Tool base class for all function-calling tools.in ai-parrot framework.
3
+ """
4
+ import importlib
5
+ import inspect
6
+ import os
7
+ from typing import Dict, Any, Union, Optional, Type
8
+ from abc import ABC, abstractmethod
9
+ from pathlib import Path
10
+ from datetime import datetime
11
+ import traceback
12
+ import logging
13
+ from dataclasses import dataclass
14
+ from urllib.parse import urlparse, urlunparse
15
+ from pydantic import BaseModel, Field
16
+ from datamodel.parsers.json import json_decoder, json_encoder, JSONContent # noqa pylint: disable=E0611
17
+ from navconfig.logging import logging
18
+ from ..conf import BASE_STATIC_URL, STATIC_DIR
19
+
20
+
21
+ logging.getLogger(name='matplotlib').setLevel(logging.INFO)
22
+ logging.getLogger(name='h5py').setLevel(logging.INFO)
23
+ logging.getLogger(name='datasets').setLevel(logging.WARNING)
24
+ logging.getLogger(name='numexpr').setLevel(logging.WARNING)
25
+ logging.getLogger(name='pymongo').setLevel(logging.WARNING)
26
+
27
+
28
+ class AbstractToolArgsSchema(BaseModel):
29
+ """Base schema for tool arguments."""
30
+ pass
31
+
32
+
33
+ class ToolResult(BaseModel):
34
+ """Standardized tool result format."""
35
+ success: bool = Field(default=True, description="Indicates if the tool executed successfully")
36
+ status: str = Field(default="success", description="Status of the operation")
37
+ result: Any = Field(description="The actual result of the tool operation")
38
+ error: Optional[str] = Field(default=None, description="Error message if any")
39
+ metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata")
40
+ timestamp: str = Field(default_factory=lambda: datetime.now().isoformat())
41
+
42
+
43
+ class AbstractTool(ABC):
44
+ """
45
+ Abstract base class for all tools in the ai-parrot framework.
46
+
47
+ This class provides a unified interface for tools that can be used by both
48
+ conversational bots and agents. It includes common functionality like:
49
+ - Name and description management
50
+ - JSON schema generation
51
+ - File path management
52
+ - Logging and error handling
53
+ - Async/sync execution support
54
+ """
55
+
56
+ # Class attributes that should be set by subclasses
57
+ name: str = None
58
+ description: str = None
59
+ args_schema: Type[BaseModel] = AbstractToolArgsSchema
60
+ return_direct: bool = False
61
+
62
+ def __init__(
63
+ self,
64
+ name: Optional[str] = None,
65
+ description: Optional[str] = None,
66
+ output_dir: Optional[Union[str, Path]] = None,
67
+ base_url: Optional[str] = None,
68
+ static_dir: Optional[Union[str, Path]] = None,
69
+ **kwargs
70
+ ):
71
+ """
72
+ Initialize the tool.
73
+
74
+ Args:
75
+ name: Tool name (defaults to class name)
76
+ description: Tool description
77
+ output_dir: Directory for output files (if tool generates files)
78
+ base_url: Base URL for serving static files
79
+ static_dir: Static directory path
80
+ **kwargs: Additional configuration
81
+ """
82
+ # Store initialization parameters for cloning
83
+ self._init_kwargs = {
84
+ 'name': name,
85
+ 'description': description,
86
+ 'output_dir': output_dir,
87
+ 'base_url': base_url,
88
+ 'static_dir': static_dir,
89
+ **kwargs
90
+ }
91
+
92
+ # Set name and description
93
+ self.name = name or self.name or self.__class__.__name__
94
+ self.description = description or self.__class__.__doc__ or f"Tool: {self.name}"
95
+
96
+ # Set up logging
97
+ self.logger = logging.getLogger(
98
+ f'{self.name}.Tool'
99
+ )
100
+
101
+ # JSON encoders/decoders
102
+ self._json_encoder = json_encoder
103
+ self._json_decoder = json_decoder
104
+ self._json = JSONContent()
105
+
106
+ # File and URL configuration
107
+ self.base_url = base_url or BASE_STATIC_URL
108
+ self.static_url = base_url or BASE_STATIC_URL
109
+ parsed = urlparse(self.static_url)
110
+ self._base_scheme_netloc = (parsed.scheme, parsed.netloc)
111
+
112
+ # Set up directories
113
+ self.static_dir = Path(static_dir or STATIC_DIR).resolve()
114
+
115
+ self.output_dir = Path(output_dir).resolve() if output_dir else self._default_output_dir()
116
+
117
+ # Ensure output directory exists if specified
118
+ if self.output_dir and not self.output_dir.exists():
119
+ self.output_dir.mkdir(parents=True, exist_ok=True)
120
+
121
+ def _default_output_dir(self) -> Optional[Path]:
122
+ """Get the default output directory for this tool type."""
123
+ # Default implementation - tools that don't need output can return None
124
+ return None
125
+
126
+ def _get_clone_kwargs(self) -> Dict[str, Any]:
127
+ """
128
+ Get the keyword arguments to use when cloning this tool.
129
+
130
+ Subclasses can override this method to customize which parameters
131
+ are cloned and which are not. By default, all initialization
132
+ parameters stored in _init_kwargs are returned.
133
+
134
+ Returns:
135
+ Dictionary of keyword arguments for tool initialization
136
+ """
137
+ return self._init_kwargs.copy()
138
+
139
+ def clone(self):
140
+ """
141
+ Create a new instance of this tool with the same configuration.
142
+
143
+ This method creates a new instance of the tool class with all the
144
+ initialization parameters that were passed to the current instance.
145
+ Subclasses can override _get_clone_kwargs() to customize which
146
+ parameters are cloned and which are not.
147
+
148
+ Returns:
149
+ New instance of the same tool class with cloned configuration
150
+
151
+ Example:
152
+ >>> dbtool = DatabaseTool(connection_string="postgresql://...")
153
+ >>> new_tool = dbtool.clone()
154
+ >>> # new_tool is a fresh instance with the same configuration
155
+ """
156
+ clone_kwargs = self._get_clone_kwargs()
157
+ return self.__class__(**clone_kwargs)
158
+
159
+ @abstractmethod
160
+ async def _execute(self, **kwargs) -> Any:
161
+ """
162
+ Execute the tool with the given arguments.
163
+ This is the main method that subclasses must implement.
164
+
165
+ Args:
166
+ **kwargs: Tool arguments
167
+
168
+ Returns:
169
+ Tool execution result
170
+ """
171
+ pass
172
+
173
+ def get_tool_schema(self) -> Dict[str, Any]:
174
+ """
175
+ Get the JSON schema for this tool.
176
+
177
+ Returns:
178
+ JSON schema dictionary compatible with LLM tool registration
179
+ """
180
+
181
+ def _enforce_no_extra_fields(definition: Any) -> None:
182
+ """Recursively set ``additionalProperties`` to ``False`` for objects.
183
+
184
+ OpenAI tools require every object schema (including nested ones) to
185
+ explicitly disallow extra properties. Pydantic's generated schema only
186
+ sets this flag at the top level, so we walk the entire schema tree and
187
+ ensure every object definition is strict.
188
+ """
189
+
190
+ if not isinstance(definition, dict):
191
+ return
192
+
193
+ if definition.get("type") == "object":
194
+ definition.setdefault("properties", {})
195
+ definition.setdefault("additionalProperties", False)
196
+
197
+ # Recurse into common schema containers
198
+ for key in ("properties", "patternProperties"):
199
+ if isinstance(definition.get(key), dict):
200
+ for sub in definition[key].values():
201
+ _enforce_no_extra_fields(sub)
202
+
203
+ for key in ("items", "additionalItems"):
204
+ _enforce_no_extra_fields(definition.get(key))
205
+
206
+ for key in ("anyOf", "oneOf", "allOf"):
207
+ if isinstance(definition.get(key), list):
208
+ for sub in definition[key]:
209
+ _enforce_no_extra_fields(sub)
210
+
211
+ # Handle $defs/definitions used by Pydantic
212
+ for key in ("$defs", "definitions"):
213
+ if isinstance(definition.get(key), dict):
214
+ for sub in definition[key].values():
215
+ _enforce_no_extra_fields(sub)
216
+
217
+ schema = {
218
+ "name": self.name,
219
+ "description": self.description,
220
+ "parameters": {
221
+ "type": "object",
222
+ "properties": {},
223
+ "required": [],
224
+ "additionalProperties": False
225
+ }
226
+ }
227
+
228
+ # If args_schema is defined, use it to build the parameters
229
+ if self.args_schema and self.args_schema != AbstractToolArgsSchema:
230
+ pydantic_schema = self.args_schema.model_json_schema()
231
+ schema["parameters"] = {
232
+ "type": "object",
233
+ "properties": pydantic_schema.get("properties", {}),
234
+ "required": pydantic_schema.get("required", []),
235
+ "additionalProperties": False,
236
+ "$defs": pydantic_schema.get("$defs", {}),
237
+ }
238
+
239
+ _enforce_no_extra_fields(schema["parameters"])
240
+ return schema
241
+
242
+ def validate_args(self, **kwargs) -> BaseModel:
243
+ """
244
+ Validate arguments using the tool's schema.
245
+
246
+ Args:
247
+ **kwargs: Arguments to validate
248
+
249
+ Returns:
250
+ Validated arguments as Pydantic model instance
251
+ """
252
+ if not self.args_schema or self.args_schema == AbstractToolArgsSchema:
253
+ # If no schema is defined, return a basic model with the kwargs
254
+ return AbstractToolArgsSchema()
255
+ try:
256
+ result = self.args_schema(**kwargs)
257
+ if not result:
258
+ self.logger.warning(
259
+ f"Validation failed for {self.name} with args: {kwargs}"
260
+ )
261
+ return result
262
+ except Exception as e:
263
+ self.logger.error(f"Validation error in {self.name}: {e}")
264
+ raise ValueError(
265
+ f"Invalid arguments for {self.name}: {e}"
266
+ ) from e
267
+
268
+
269
+ async def execute(self, *args, **kwargs) -> ToolResult:
270
+ """
271
+ Execute the tool with error handling and result standardization.
272
+
273
+ Args:
274
+ **kwargs: Tool arguments
275
+
276
+ Returns:
277
+ Standardized ToolResult
278
+
279
+ TODO: Use the Global Registry to share data between tools.
280
+ """
281
+ try:
282
+ self.logger.notice(f"Executing tool: {self.name}")
283
+
284
+ # Validate arguments
285
+ validated_args = self.validate_args(**kwargs)
286
+
287
+ # Execute the tool
288
+ if hasattr(validated_args, 'model_dump'):
289
+ result = await self._execute(*args, **validated_args.model_dump())
290
+ else:
291
+ result = await self._execute(*args, **kwargs)
292
+
293
+ # if is an toolResult, return it directly
294
+ if isinstance(result, ToolResult):
295
+ return result
296
+ elif isinstance(result, dict) and 'status' in result and 'result' in result:
297
+ try:
298
+ return ToolResult(**result)
299
+ except Exception as e:
300
+ self.logger.error(f"Error creating ToolResult from dict: {e}")
301
+ return ToolResult(
302
+ status="done_with_errors",
303
+ result=result.get('result', []),
304
+ error=f"Error creating ToolResult: {e}",
305
+ metadata=result.get('metadata', {})
306
+ )
307
+ if result is None:
308
+ raise ValueError(
309
+ "Tool execution returned None, expected a result."
310
+ )
311
+
312
+ self.logger.info(
313
+ f"Tool {self.name} executed successfully"
314
+ )
315
+ # print('TYPE > ', type(result), ' RESULT > ', result)
316
+
317
+ return ToolResult(
318
+ status="success",
319
+ result=result,
320
+ metadata={
321
+ "tool_name": self.name,
322
+ "execution_time": datetime.now().isoformat()
323
+ }
324
+ )
325
+
326
+ except Exception as e:
327
+ print('ERROR')
328
+ print(f'============ {e} ============')
329
+ error_msg = f"Error in {self.name}: {str(e)}"
330
+ self.logger.error(error_msg)
331
+ self.logger.error(traceback.format_exc())
332
+
333
+ return ToolResult(
334
+ status="error",
335
+ result=None,
336
+ error=error_msg,
337
+ metadata={
338
+ "tool_name": self.name,
339
+ "error_type": type(e).__name__
340
+ }
341
+ )
342
+
343
+ run = execute # Alias for compatibility with sync code
344
+
345
+ # Utility methods for file handling (inherited from BaseAbstractTool)
346
+ def to_static_url(self, file_path: Union[str, Path]) -> str:
347
+ """
348
+ Convert an absolute file path to a static URL.
349
+
350
+ Args:
351
+ file_path: Absolute path to the file
352
+
353
+ Returns:
354
+ URL-based path for serving the static file
355
+ """
356
+ if not self.static_dir:
357
+ return str(file_path)
358
+
359
+ file_path = Path(file_path)
360
+
361
+ try:
362
+ relative_path = file_path.relative_to(self.static_dir)
363
+ return f"{self.static_url.rstrip('/')}/{relative_path}"
364
+ except ValueError:
365
+ self.logger.warning(
366
+ f"File {file_path} is not within static directory {self.static_dir}"
367
+ )
368
+ return str(file_path)
369
+
370
+ def relative_url(self, url: str) -> str:
371
+ """
372
+ Convert an absolute URL to a relative URL based on the base URL.
373
+
374
+ Args:
375
+ url: Absolute URL to convert
376
+
377
+ Returns:
378
+ Relative URL based on the base URL
379
+ """
380
+ parts = urlparse(url)
381
+ if not parts.scheme or not parts.netloc:
382
+ return url
383
+
384
+ if (parts.scheme, parts.netloc) == self._base_scheme_netloc:
385
+ return urlunparse((
386
+ "", "", parts.path, parts.params, parts.query, parts.fragment
387
+ ))
388
+ return url
389
+
390
+ def generate_filename(
391
+ self,
392
+ prefix: str = "output",
393
+ extension: str = "",
394
+ include_timestamp: bool = True
395
+ ) -> str:
396
+ """
397
+ Generate a unique filename with optional timestamp.
398
+
399
+ Args:
400
+ prefix: File prefix
401
+ extension: File extension (with or without dot)
402
+ include_timestamp: Whether to include timestamp
403
+
404
+ Returns:
405
+ Generated filename
406
+ """
407
+ if include_timestamp:
408
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
409
+ filename = f"{prefix}_{timestamp}"
410
+ else:
411
+ filename = prefix
412
+
413
+ if extension:
414
+ if not extension.startswith('.'):
415
+ extension = f".{extension}"
416
+ filename += extension
417
+
418
+ return filename
419
+
420
+ def validate_output_path(self, file_path: Union[str, Path]) -> Path:
421
+ """
422
+ Validate and ensure the output path is within allowed directories.
423
+
424
+ Args:
425
+ file_path: Path to validate
426
+
427
+ Returns:
428
+ Validated Path object
429
+
430
+ Raises:
431
+ ValueError: If path is outside allowed directories
432
+ """
433
+ if not self.static_dir:
434
+ return Path(file_path).resolve()
435
+
436
+ file_path = Path(file_path).resolve()
437
+
438
+ try:
439
+ file_path.relative_to(self.static_dir.resolve())
440
+ except ValueError as e:
441
+ raise ValueError(
442
+ f"Output path {file_path} must be within static directory {self.static_dir}"
443
+ ) from e
444
+
445
+ return file_path
446
+
447
+ def __str__(self) -> str:
448
+ return f"{self.name}: {self.description}"
449
+
450
+ def __repr__(self) -> str:
451
+ return f"<{self.__class__.__name__}(name='{self.name}')>"
452
+
453
+
454
+ @dataclass
455
+ class ToolInfo:
456
+ """Information about a discovered tool."""
457
+ class_name: str
458
+ module_name: str
459
+ description: str
460
+ tool_name: str
461
+ file_path: str
462
+ args_schema: Optional[Dict[str, Any]] = None
463
+
464
+
465
+ # Tool Registry for easy tool management
466
+ class ToolRegistry:
467
+ """Registry for managing available tools."""
468
+
469
+ def __init__(self):
470
+ self._tools = {}
471
+ self.tools_package_path = 'parrot/tools'
472
+ self.discovered_tools: Dict[str, ToolInfo] = {}
473
+ self.loaded_classes: Dict[str, Type[AbstractTool]] = {}
474
+
475
+ def _create_tool_info(
476
+ self,
477
+ cls: Type[AbstractTool],
478
+ class_name: str,
479
+ module_name: str,
480
+ file_path: str
481
+ ) -> ToolInfo:
482
+ """Create ToolInfo object from a tool class."""
483
+ try:
484
+ # Get tool description
485
+ description = getattr(cls, 'description', cls.__doc__ or 'No description available')
486
+
487
+ # Get tool name
488
+ tool_name = getattr(cls, 'name', class_name)
489
+
490
+ # Get args schema if available
491
+ args_schema = None
492
+ if hasattr(cls, 'args_schema') and cls.args_schema:
493
+ try:
494
+ args_schema = cls.args_schema.model_json_schema()
495
+ except Exception as e:
496
+ logging.debug(f"Could not get schema for {class_name}: {e}")
497
+
498
+ return ToolInfo(
499
+ class_name=class_name,
500
+ module_name=module_name,
501
+ description=description,
502
+ tool_name=tool_name,
503
+ file_path=file_path,
504
+ args_schema=args_schema
505
+ )
506
+
507
+ except Exception as e:
508
+ logging.error(f"Error creating tool info for {class_name}: {e}")
509
+ return ToolInfo(
510
+ class_name=class_name,
511
+ module_name=module_name,
512
+ description="Error loading description",
513
+ tool_name=class_name,
514
+ file_path=file_path
515
+ )
516
+
517
+ def _process_python_file(self, file_path: Path, tools_dir: Path) -> None:
518
+ """Process a single Python file to find tool classes."""
519
+ try:
520
+ # Get relative path from tools directory
521
+ relative_to_tools = file_path.relative_to(tools_dir)
522
+ # Build module name using the configured tools_package_path
523
+ base_package = self.tools_package_path.replace('/', '.') # 'parrot.tools'
524
+
525
+ # Add any subdirectories and filename
526
+ if relative_to_tools.parent != Path('.'):
527
+ # Handle subdirectories: e.g., 'analysis/correlation.py' -> 'parrot.tools.analysis.correlation'
528
+ subpath = str(relative_to_tools.parent).replace(os.sep, '.')
529
+ module_name = f"{base_package}.{subpath}.{relative_to_tools.stem}"
530
+ else:
531
+ # Direct file in tools directory: 'google.py' -> 'parrot.tools.google'
532
+ module_name = f"{base_package}.{relative_to_tools.stem}"
533
+
534
+ logging.debug(f"Processing module: {module_name}")
535
+
536
+ # Import the module
537
+ module = importlib.import_module(module_name)
538
+
539
+ # Find tool classes in the module
540
+ for name, obj in inspect.getmembers(module, inspect.isclass):
541
+ if issubclass(obj, AbstractTool):
542
+ if name == 'AbstractTool':
543
+ continue
544
+ tool_info = self._create_tool_info(
545
+ obj,
546
+ name,
547
+ module_name,
548
+ str(file_path)
549
+ )
550
+ self.discovered_tools[name] = tool_info
551
+ self.loaded_classes[name] = obj
552
+ logging.debug(f"Found tool: {name}")
553
+
554
+ except Exception as e:
555
+ logging.error(f"Error processing {file_path}: {e}")
556
+
557
+ def discover_tools(self) -> Dict[str, ToolInfo]:
558
+ """
559
+ Discover all tool classes in the tools package.
560
+
561
+ Returns:
562
+ Dict mapping class names to ToolInfo objects
563
+ """
564
+ tools_dir = Path(self.tools_package_path).resolve()
565
+ if not tools_dir.exists():
566
+ logging.warning(f"Tools directory '{tools_dir}' does not exist")
567
+ return {}
568
+
569
+ # Clear previous discoveries
570
+ self.discovered_tools.clear()
571
+ self.loaded_classes.clear()
572
+
573
+ # walk through the tools directory and find all .py files
574
+ for file_path in tools_dir.rglob('*.py'):
575
+ if file_path.name.startswith('_'):
576
+ continue
577
+ self._process_python_file(file_path, tools_dir)
578
+
579
+ logging.info(f"Discovered {len(self.discovered_tools)} tools")
580
+ return self.discovered_tools
581
+
582
+ def register_toolkit(self, toolkit: Type[AbstractTool], prefix: str = ""):
583
+ """
584
+ Register all tools from a toolkit in a tool registry.
585
+
586
+ Args:
587
+ registry: Tool registry instance
588
+ toolkit: Toolkit instance
589
+ prefix: Optional prefix for tool names
590
+ """
591
+ tools = toolkit.get_tools()
592
+ for tool in tools:
593
+ tool_name = f"{prefix}{tool.name}" if prefix else tool.name
594
+ self.register(tool.__class__, tool_name)
595
+
596
+ def register(self, tool_class: Type[AbstractTool], name: Optional[str] = None):
597
+ """Register a tool class."""
598
+ tool_name = name or tool_class.name or tool_class.__name__
599
+ self._tools[tool_name] = tool_class
600
+
601
+ def register_by_name(self, tool_name: str):
602
+ """Register a tool class by its name."""
603
+ if tool_name in self._tools:
604
+ raise ValueError(f"Tool '{tool_name}' is already registered")
605
+ # use importlib to dynamically import the tool class
606
+ file_name = tool_name.lower().replace('tool', '')
607
+ try:
608
+ module = __import__(f"parrot.tools.{file_name}", fromlist=[tool_name])
609
+ tool_class = getattr(module, tool_name)
610
+ except (ImportError, AttributeError) as e:
611
+ raise ValueError(f"Could not import tool '{tool_name}': {e}")
612
+ if not issubclass(tool_class, AbstractTool):
613
+ raise ValueError(f"Tool '{tool_name}' must be a subclass of AbstractTool")
614
+ if tool_name in self._tools:
615
+ raise ValueError(f"Tool '{tool_name}' is already registered")
616
+ # Register the tool class
617
+ self._tools[tool_name] = tool_class
618
+
619
+ def get_tool(self, name: str, **kwargs) -> AbstractTool:
620
+ """Get an instance of a tool by name."""
621
+ if name not in self._tools:
622
+ raise ValueError(f"Tool '{name}' not found in registry")
623
+
624
+ tool_class = self._tools[name]
625
+ return tool_class(**kwargs)
626
+
627
+ def list_tools(self) -> Dict[str, str]:
628
+ """List all registered tools with their descriptions."""
629
+ return {
630
+ name: getattr(tool_class, 'description', 'No description')
631
+ for name, tool_class in self._tools.items()
632
+ }
633
+
634
+ def get_all_schemas(self) -> Dict[str, Dict[str, Any]]:
635
+ """Get schemas for all registered tools."""
636
+ schemas = {}
637
+ for name, tool_class in self._tools.items():
638
+ try:
639
+ # Create a temporary instance to get schema
640
+ temp_instance = tool_class()
641
+ schemas[name] = temp_instance.get_tool_schema()
642
+ except Exception as e:
643
+ logging.error(f"Error getting schema for tool {name}: {e}")
644
+ return schemas