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,594 @@
1
+ # agents/registry.py
2
+ """
3
+ Agent Auto-Registration System for AI-Parrot.
4
+
5
+ This module provides multiple approaches for automatically discovering
6
+ and registering agents from the agents/ directory.
7
+ """
8
+ from __future__ import annotations
9
+ import sys
10
+ import asyncio
11
+ from typing import Dict, Iterable, List, Type, Set, Union, Optional, Any, Protocol
12
+ from pathlib import Path
13
+ from types import ModuleType
14
+ import importlib
15
+ import inspect
16
+ from dataclasses import dataclass, field
17
+ import yaml
18
+ import hashlib
19
+ from navconfig.logging import logging
20
+ from navconfig import BASE_DIR
21
+ from ..bots.abstract import AbstractBot
22
+
23
+
24
+ class AgentFactory(Protocol):
25
+ """Protocol for agent factory callable."""
26
+ def __call__(self, **kwargs: Any) -> AbstractBot: ...
27
+
28
+
29
+ @dataclass(slots=True)
30
+ class BotMetadata:
31
+ """
32
+ Metadata about a discovered Bot or Agent.
33
+
34
+ This class holds information about agents found during discovery,
35
+ making it easier to manage and validate them before registration.
36
+ """
37
+ name: str
38
+ factory: Union[Type[AbstractBot], AgentFactory]
39
+ module_path: str
40
+ file_path: Path
41
+ singleton: bool = False
42
+ tags: Optional[Set[str]] = field(default_factory=set)
43
+ priority: int = 0
44
+ at_startup: bool = False
45
+ dependencies: List[str] = field(default_factory=list)
46
+ startup_config: Dict[str, Any] = field(default_factory=dict) # Config for startup instantiation
47
+ _instance: Optional[AbstractBot] = None
48
+ _lock: asyncio.Lock = field(default_factory=asyncio.Lock, repr=False)
49
+
50
+ def __post_init__(self):
51
+ """Validate bot metadata after creation."""
52
+ if not issubclass(self.factory, AbstractBot):
53
+ raise ValueError(
54
+ f"Bot {self.name} must inherit from AbstractBot"
55
+ )
56
+ # If at_startup=True, automatically make it singleton
57
+ if self.at_startup:
58
+ self.singleton = True
59
+
60
+ async def get_instance(self, *args, **kwargs) -> AbstractBot:
61
+ """
62
+ Get or create an instance of the bot.
63
+
64
+ This implements lazy instantiation - instances are only created when needed.
65
+ For singleton bots, the same instance is returned on subsequent calls.
66
+ """
67
+ # Singleton path
68
+ if self.singleton and self._instance is not None:
69
+ return self._instance
70
+
71
+ async with self._lock:
72
+ # Double-check pattern for singletons
73
+ if self.singleton and self._instance is not None:
74
+ return self._instance
75
+
76
+ # Merge startup config with runtime kwargs
77
+ merged_kwargs = {**self.startup_config, **kwargs}
78
+ # Create new instance
79
+ instance = self.factory(name=self.name, **merged_kwargs)
80
+ if not isinstance(instance, AbstractBot):
81
+ raise ValueError(
82
+ f"Factory for {self.name} returned {type(instance)!r}, expected AbstractBot."
83
+ )
84
+ # Configure instance if needed:
85
+ if not self.at_startup:
86
+ await instance.configure()
87
+ # Store instance if singleton
88
+ if self.singleton:
89
+ self._instance = instance
90
+
91
+ return instance
92
+
93
+ @dataclass
94
+ class BotConfig:
95
+ """Configuration for the bot in config-based discovery."""
96
+ name: str
97
+ class_name: str
98
+ module: str
99
+ enabled: bool = True
100
+ config: Dict[str, Any] = field(default_factory=dict)
101
+ tags: Optional[Set[str]] = field(default_factory=set)
102
+ singleton: bool = False
103
+ at_startup: bool = False
104
+ startup_config: Dict[str, Any] = field(default_factory=dict)
105
+ priority: int = 0
106
+
107
+
108
+ class AgentRegistry:
109
+ """
110
+ Central registry for managing Bo/Agent discovery and registration.
111
+
112
+ This class maintains a registry of all discovered agents and provides
113
+ methods for discovering, validating, and instantiating them.
114
+
115
+ - register(): programmatic registration
116
+ - register_agent decorator: declarative registration on class definition
117
+
118
+ We can use several strategies for discovery:
119
+ - decorators to mark classes for auto-registration.
120
+ - Configuration-Based Discovery, use a YAML config to define agents.
121
+
122
+ Decorator Usage:
123
+ @register_agent(name="MySpecialAgent", priority=10)
124
+ class MyAgent(AbstractBot):
125
+ pass
126
+
127
+ # Programmatic registration
128
+ agent_registry.register("CustomAgent", CustomAgentClass)
129
+
130
+ Configuration agents.yaml:
131
+ agents:
132
+ - name: "ReportGenerator"
133
+ class_name: "ReportGeneratorAgent"
134
+ module: "agents.reporting"
135
+ enabled: true
136
+ config:
137
+ templates_dir: "./templates"
138
+ - name: "DataAnalyzer"
139
+ class_name: "DataAnalyzerAgent"
140
+ module: "agents.analysis"
141
+ enabled: true
142
+
143
+ # Get instances
144
+ agent = await agent_registry.get_instance("MyAgent")
145
+ """
146
+
147
+ def __init__(
148
+ self,
149
+ agents_dir: Optional[Path] = None,
150
+ *,
151
+ extra_agent_dirs: Optional[Iterable[Path]] = None,
152
+ ):
153
+ self.logger = logging.getLogger('Parrot.AgentRegistry')
154
+ self.agents_dir = agents_dir or BASE_DIR / "agents"
155
+ self._registered_agents: Dict[str, BotMetadata] = {}
156
+ self._config_file: Optional[Path] = None
157
+ self._discovery_paths: List[Path] = []
158
+
159
+ # Ensure primary discovery directory exists
160
+ primary_dir = self._prepare_discovery_dir(self.agents_dir)
161
+ self._discovery_paths.append(primary_dir)
162
+
163
+ self._extra_agent_dirs: List[Path] = []
164
+ if extra_agent_dirs:
165
+ for directory in extra_agent_dirs:
166
+ prepared_dir = self._prepare_discovery_dir(directory)
167
+ self._extra_agent_dirs.append(prepared_dir)
168
+ self._discovery_paths.append(prepared_dir)
169
+ # Create config file if it doesn't exist
170
+ self._config_file: Optional[Path] = self.agents_dir / "agents.yaml"
171
+ if not self._config_file.exists():
172
+ self._config_file.write_text(
173
+ "# Auto-generated agents configuration\nagents: []\n"
174
+ )
175
+ self.logger.notice(
176
+ f"AgentRegistry initialized with agents_dir={self.agents_dir}, config_file={self._config_file}"
177
+ )
178
+
179
+ def _prepare_discovery_dir(self, directory: Path) -> Path:
180
+ """Ensure a discovery directory exists and is importable."""
181
+ resolved = Path(directory).resolve()
182
+ resolved.mkdir(parents=True, exist_ok=True)
183
+ init_file = resolved / "__init__.py"
184
+ if not init_file.exists():
185
+ init_file.write_text("# Auto-generated agents module")
186
+ if str(resolved) not in sys.path:
187
+ sys.path.append(str(resolved))
188
+ return resolved
189
+
190
+ def get_bot_instance(self, name: str) -> Optional[AbstractBot]:
191
+ """Get an instantiated bot by name."""
192
+ return self._registered_agents.get(name)
193
+
194
+ def get_metadata(self, name: str) -> Optional[BotMetadata]:
195
+ return self._registered_agents.get(name)
196
+
197
+ def register(
198
+ self,
199
+ name: str,
200
+ factory: Type[AbstractBot],
201
+ *,
202
+ singleton: bool = False,
203
+ tags: Optional[Iterable[str]] = None,
204
+ priority: int = 0,
205
+ dependencies: Optional[List[str]] = None,
206
+ replace: bool = False,
207
+ at_startup: bool = False,
208
+ startup_config: Optional[Dict[str, Any]] = None,
209
+ **kwargs: Any
210
+ ) -> None:
211
+ """
212
+ Register a bot class with the registry.
213
+
214
+ This is the core registration method that both decorator and config-based
215
+ registration ultimately call.
216
+
217
+ Args:
218
+ name: Unique name for the bot
219
+ factory: Bot class (subclass of AbstractBot)
220
+ singleton: Whether to enforce singleton instance
221
+ tags: Optional tags for categorization
222
+ priority: Registration priority (higher = earlier)
223
+ dependencies: List of required dependencies
224
+ replace: Whether to replace an existing registration
225
+ startup_config: Configuration to use during startup instantiation
226
+ **kwargs: Additional metadata
227
+ """
228
+ if name in self._registered_agents and not replace:
229
+ self.logger.warning(
230
+ f"Bot {name} already registered, use replace=True to overwrite"
231
+ )
232
+ return
233
+
234
+ if not issubclass(factory, AbstractBot):
235
+ raise ValueError(
236
+ f"Bot {name} must inherit from AbstractBot"
237
+ )
238
+
239
+ # Get module information
240
+ module = inspect.getmodule(factory)
241
+ module_path = module.__name__ if module else "unknown"
242
+ file_path = Path(module.__file__) if module and module.__file__ else Path("unknown")
243
+
244
+ if not startup_config:
245
+ startup_config = {}
246
+ merged_kwargs = {**startup_config, **kwargs}
247
+
248
+ metadata = BotMetadata(
249
+ name=name,
250
+ factory=factory,
251
+ module_path=module_path,
252
+ file_path=file_path,
253
+ singleton=singleton,
254
+ at_startup=at_startup,
255
+ startup_config=merged_kwargs or {},
256
+ tags=set(tags or []),
257
+ priority=priority,
258
+ dependencies=dependencies or [],
259
+ )
260
+
261
+ self._registered_agents[name] = metadata
262
+ self.logger.info(
263
+ f"Registered bot: {name}"
264
+ )
265
+
266
+ def has(self, name: str) -> bool:
267
+ return name in self._registered_agents
268
+
269
+ async def get_instance(self, name: str, **kwargs) -> Optional[AbstractBot]:
270
+ """
271
+ Get an instance of a registered bot.
272
+
273
+ This method handles lazy instantiation - bots are only created when needed.
274
+
275
+ Args:
276
+ name: Name of the bot to instantiate
277
+ **kwargs: Additional arguments to pass to the bot constructor
278
+
279
+ Returns:
280
+ Bot instance or None if not found
281
+ """
282
+ if name not in self._registered_agents:
283
+ self.logger.warning(f"Bot {name} not found in registry")
284
+ return None
285
+
286
+ metadata = self._registered_agents[name]
287
+ try:
288
+ instance = await metadata.get_instance(**kwargs)
289
+ self.logger.debug(f"Retrieved instance for bot: {name}")
290
+ return instance
291
+ except Exception as e:
292
+ self.logger.error(f"Failed to instantiate bot {name}: {str(e)}")
293
+ return None
294
+
295
+ def load_config(self) -> List[BotConfig]:
296
+ """Load bot configuration from YAML file."""
297
+ if not self._config_file or not self._config_file.exists():
298
+ self.logger.debug(
299
+ "No config file found, skipping config-based discovery"
300
+ )
301
+ return []
302
+
303
+ try:
304
+ with open(self._config_file, 'r') as f:
305
+ config_data = yaml.safe_load(f)
306
+
307
+ configs = []
308
+ for agent_data in config_data.get('agents', []):
309
+ try:
310
+ config = BotConfig(**agent_data)
311
+ if config.enabled:
312
+ configs.append(config)
313
+ except Exception as e:
314
+ self.logger.error(
315
+ f"Invalid config entry: {agent_data}, error: {e}"
316
+ )
317
+ continue
318
+
319
+ return configs
320
+
321
+ except ImportError:
322
+ self.logger.error("PyYAML not installed. Install with: pip install pyyaml")
323
+ return []
324
+ except Exception as e:
325
+ self.logger.error(f"Failed to load config: {str(e)}")
326
+ return []
327
+
328
+ def discover_config_agents(self) -> List[BotMetadata]:
329
+ """
330
+ Register agents from configuration file.
331
+
332
+ This method loads the config file and registers all enabled agents.
333
+
334
+ Returns:
335
+ Number of agents successfully registered from config
336
+ """
337
+ configs = self.load_config()
338
+ registered_count = 0
339
+
340
+ for config in configs:
341
+ if not config.enabled:
342
+ continue
343
+
344
+ try:
345
+ # Import the module
346
+ module = importlib.import_module(config.module)
347
+
348
+ # Get the class
349
+ agent_class = getattr(module, config.class_name)
350
+
351
+ # Validate it's an AbstractBot subclass
352
+ if not issubclass(agent_class, AbstractBot):
353
+ self.logger.error(
354
+ f"{config.class_name} is not an AbstractBot subclass"
355
+ )
356
+ continue
357
+
358
+ # Register using core register method
359
+ self.register(
360
+ name=config.name,
361
+ factory=agent_class,
362
+ singleton=config.singleton,
363
+ tags=config.tags,
364
+ priority=config.priority,
365
+ at_startup=config.at_startup,
366
+ startup_config=config.config,
367
+ replace=True
368
+ )
369
+
370
+ registered_count += 1
371
+ self.logger.info(
372
+ f"Registered bot from config: {config.name}"
373
+ )
374
+
375
+ except Exception as e:
376
+ self.logger.error(
377
+ f"Failed to load bot {config.name}: {str(e)}"
378
+ )
379
+ continue
380
+
381
+ return registered_count
382
+
383
+ def _import_module_from_path(
384
+ self,
385
+ path: Path,
386
+ *,
387
+ base_dir: Optional[Path] = None,
388
+ package_hint: str = "parrot.dynamic_agents",
389
+ ) -> ModuleType:
390
+ """
391
+ Import a Python module from an arbitrary filesystem path.
392
+ Ensures decorators run at import time.
393
+ """
394
+ base = (base_dir or self.agents_dir).resolve()
395
+ resolved_path = path.resolve()
396
+ try:
397
+ rel = resolved_path.relative_to(base)
398
+ except ValueError:
399
+ rel = Path(resolved_path.name)
400
+ rel_path = rel if isinstance(rel, Path) else Path(rel)
401
+ module_suffix = ".".join(rel_path.with_suffix('').parts)
402
+ if module_suffix:
403
+ mod_name = f"{package_hint}.{module_suffix}"
404
+ else:
405
+ mod_name = package_hint
406
+
407
+ spec = importlib.util.spec_from_file_location(mod_name, str(path))
408
+ if spec is None or spec.loader is None:
409
+ raise ImportError(
410
+ f"Could not load spec for {path}"
411
+ )
412
+
413
+ module = importlib.util.module_from_spec(spec)
414
+ sys.modules[mod_name] = module
415
+ spec.loader.exec_module(module) # type: ignore[attr-defined]
416
+ self.logger.debug(
417
+ f"Imported agent module: {mod_name} from {path}"
418
+ )
419
+ return module
420
+
421
+ def _namespace_for_directory(self, directory: Path) -> str:
422
+ digest = hashlib.md5(str(directory.resolve()).encode('utf-8')).hexdigest()
423
+ return f"parrot.dynamic_agents.dir_{digest}"
424
+
425
+ def _load_modules_from_directory(self, directory: Path) -> int:
426
+ if not directory.exists() or not directory.is_dir():
427
+ self.logger.debug(
428
+ f"Agents directory {directory} does not exist, skipping"
429
+ )
430
+ return 0
431
+
432
+ package_hint = self._namespace_for_directory(directory)
433
+ module_files = list(directory.glob("*.py"))
434
+ imported_count = 0
435
+
436
+ for file_path in module_files:
437
+ if file_path.name == "__init__.py":
438
+ continue # Skip __init__.py
439
+
440
+ try:
441
+ self._import_module_from_path(
442
+ file_path,
443
+ base_dir=directory,
444
+ package_hint=package_hint
445
+ )
446
+ imported_count += 1
447
+ except Exception as e:
448
+ self.logger.error(f"Failed to import {file_path}: {e}")
449
+ return imported_count
450
+
451
+ async def load_modules(self) -> int:
452
+ """
453
+ Dynamically import all Python modules from every discovery directory.
454
+
455
+ This triggers any decorators in those modules to register agents.
456
+ """
457
+ total_imported = 0
458
+ for directory in self._discovery_paths:
459
+ total_imported += self._load_modules_from_directory(directory)
460
+
461
+ self.logger.info(
462
+ f"Discovered (decorator) agent modules: {total_imported} across {len(self._discovery_paths)} directories"
463
+ )
464
+ return total_imported
465
+
466
+ def register_bot_decorator(
467
+ self,
468
+ *,
469
+ name: Optional[str] = None,
470
+ priority: int = 0,
471
+ dependencies: Optional[List[str]] = None,
472
+ singleton: bool = False,
473
+ at_startup: bool = False,
474
+ startup_config: Optional[Dict[str, Any]] = None,
475
+ tags: Optional[Iterable[str]] = None,
476
+ **kwargs
477
+ ):
478
+ """
479
+ Decorator to register an AbstractBot subclass.
480
+
481
+ This decorator immediately calls self.register() to register the agent,
482
+ rather than storing it separately for later processing.
483
+
484
+ Args:
485
+ name: Agent name (defaults to class name)
486
+ priority: Registration priority (higher = earlier)
487
+ dependencies: List of required dependencies
488
+ singleton: Whether to enforce singleton instance
489
+ tags: Optional tags for categorization
490
+ **kwargs: Additional registration parameters
491
+
492
+ Usage:
493
+ @register_agent(name="CriticalAgent", at_startup=True, startup_config={"db_pool_size": 10})
494
+ class MyBot(AbstractBot):
495
+ pass
496
+ """
497
+ def _decorator(cls: Type[AbstractBot]) -> Type[AbstractBot]:
498
+ if not inspect.isclass(cls):
499
+ raise TypeError("@register_agent can only be used on classes.")
500
+
501
+ if not issubclass(cls, AbstractBot):
502
+ raise TypeError("@register_agent can only be used on AbstractBot subclasses.")
503
+
504
+ # Determine agent name
505
+ bot_name = (name or cls.__name__).strip()
506
+ # Register immediately using the core register method
507
+ self.register(
508
+ name=bot_name,
509
+ factory=cls,
510
+ singleton=singleton,
511
+ at_startup=at_startup,
512
+ startup_config=startup_config,
513
+ tags=tags,
514
+ priority=priority,
515
+ dependencies=dependencies,
516
+ **kwargs
517
+ )
518
+
519
+ # Mark the class with metadata for introspection
520
+ cls._parrot_agent_metadata = self._registered_agents[bot_name]
521
+
522
+ return cls
523
+
524
+ return _decorator
525
+
526
+ def list_bots_by_priority(self) -> List[BotMetadata]:
527
+ """Get all registered bots sorted by priority (highest first)."""
528
+ return sorted(
529
+ self._registered_agents.values(),
530
+ key=lambda x: x.priority,
531
+ reverse=True
532
+ )
533
+
534
+ def get_bots_by_tag(self, tag: str) -> List[BotMetadata]:
535
+ """Get all bots that have a specific tag."""
536
+ return [
537
+ metadata for metadata in self._registered_agents.values()
538
+ if tag in metadata.tags
539
+ ]
540
+
541
+ def clear_registry(self) -> None:
542
+ """Clear all registered bots. Useful for testing."""
543
+ self._registered_agents.clear()
544
+ self.logger.info("Registry cleared")
545
+
546
+ def get_registration_info(self) -> Dict[str, Any]:
547
+ """Get detailed information about the registry state."""
548
+ return {
549
+ "total_registered": len(self._registered_agents),
550
+ "by_priority": {
551
+ metadata.name: metadata.priority
552
+ for metadata in self._registered_agents.values()
553
+ },
554
+ "by_tags": {
555
+ tag: [name for name, metadata in self._registered_agents.items() if tag in metadata.tags]
556
+ for tag in set().union(*(metadata.tags for metadata in self._registered_agents.values()))
557
+ },
558
+ "singletons": [
559
+ name for name, metadata in self._registered_agents.items()
560
+ if metadata.singleton
561
+ ]
562
+ }
563
+
564
+ async def instantiate_startup_agents(self, app: Optional[Any] = None, **kwargs: Any) -> Dict[str, Any]:
565
+ """
566
+ Create instances for agents marked at_startup=True (implies singleton).
567
+ """
568
+ results = {}
569
+ startup_agents = [bot for bot in self.list_bots_by_priority() if bot.at_startup]
570
+
571
+ startup_agents.sort(
572
+ key=lambda meta: meta.priority,
573
+ reverse=True
574
+ )
575
+ for metadata in startup_agents:
576
+ try:
577
+ instance = await metadata.get_instance(**kwargs)
578
+ if callable(getattr(instance, 'configure', None)):
579
+ await instance.configure(app)
580
+ results[metadata.name] = {
581
+ "status": "success",
582
+ "instance": instance,
583
+ "instance_id": id(instance),
584
+ "priority": metadata.priority
585
+ }
586
+ except Exception as e:
587
+ self.logger.error(
588
+ f"Failed startup instantiate {metadata.name}: {e}"
589
+ )
590
+ results[metadata.name] = {
591
+ "status": "error",
592
+ "error": str(e)
593
+ }
594
+ return results