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
parrot/a2a/client.py ADDED
@@ -0,0 +1,658 @@
1
+ # parrot/a2a/client.py
2
+ """
3
+ A2A Client - Connect to remote A2A agents from AI-Parrot.
4
+ """
5
+ from __future__ import annotations
6
+ import json
7
+ import asyncio
8
+ from typing import Dict, List, Optional, Any, AsyncIterator, Type
9
+ from dataclasses import dataclass, field
10
+ import aiohttp
11
+ from pydantic import BaseModel, Field
12
+ from navconfig.logging import logging
13
+ from ..tools.abstract import AbstractTool, AbstractToolArgsSchema
14
+ from .models import (
15
+ AgentCard,
16
+ AgentSkill,
17
+ Task,
18
+ TaskStatus,
19
+ TaskState,
20
+ Artifact,
21
+ Message,
22
+ Part,
23
+ )
24
+
25
+
26
+
27
+ @dataclass
28
+ class A2AAgentConnection:
29
+ """Represents a connection to a remote A2A agent."""
30
+ url: str
31
+ card: AgentCard
32
+ client: "A2AClient"
33
+ name: str = ""
34
+
35
+ def __post_init__(self):
36
+ self.name = self.card.name
37
+
38
+
39
+ class A2AClient:
40
+ """
41
+ Client for communicating with remote A2A agents.
42
+
43
+ Example:
44
+ async with A2AClient("https://remote-agent:8080") as client:
45
+ # Discover agent
46
+ card = await client.discover()
47
+ print(f"Connected to: {card.name}")
48
+
49
+ # Send message
50
+ task = await client.send_message("Hello!")
51
+ print(task.artifacts[0].parts[0].text)
52
+
53
+ # Stream response
54
+ async for chunk in client.stream_message("Explain quantum computing"):
55
+ print(chunk, end="", flush=True)
56
+ """
57
+
58
+ def __init__(
59
+ self,
60
+ base_url: str,
61
+ *,
62
+ timeout: float = 60.0,
63
+ headers: Optional[Dict[str, str]] = None,
64
+ auth_token: Optional[str] = None,
65
+ api_key: Optional[str] = None,
66
+ ):
67
+ """
68
+ Initialize A2A client.
69
+
70
+ Args:
71
+ base_url: Base URL of the A2A agent (e.g., "https://agent.example.com")
72
+ timeout: Request timeout in seconds
73
+ headers: Additional headers to send with requests
74
+ auth_token: Bearer token for authentication
75
+ api_key: API key for authentication (sent as X-API-Key header)
76
+ """
77
+ self.base_url = base_url.rstrip("/")
78
+ self.timeout = aiohttp.ClientTimeout(total=timeout)
79
+ self._session: Optional[aiohttp.ClientSession] = None
80
+ self._agent_card: Optional[AgentCard] = None
81
+ self._owns_session = False
82
+
83
+ # Build headers
84
+ self.headers = {"Content-Type": "application/json"}
85
+ if headers:
86
+ self.headers.update(headers)
87
+ if auth_token:
88
+ self.headers["Authorization"] = f"Bearer {auth_token}"
89
+ if api_key:
90
+ self.headers["X-API-Key"] = api_key
91
+
92
+ self.logger = logging.getLogger(f"A2AClient.{base_url}")
93
+
94
+ async def __aenter__(self) -> "A2AClient":
95
+ await self.connect()
96
+ return self
97
+
98
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
99
+ await self.disconnect()
100
+
101
+ async def connect(self, session: Optional[aiohttp.ClientSession] = None) -> None:
102
+ """Establish connection and discover remote agent."""
103
+ if session:
104
+ self._session = session
105
+ self._owns_session = False
106
+ else:
107
+ self._session = aiohttp.ClientSession(
108
+ timeout=self.timeout,
109
+ headers=self.headers
110
+ )
111
+ self._owns_session = True
112
+
113
+ # Discover agent card
114
+ self._agent_card = await self.discover()
115
+ self.logger.info(f"Connected to A2A agent: {self._agent_card.name}")
116
+
117
+ async def disconnect(self) -> None:
118
+ """Close the connection."""
119
+ if self._session and self._owns_session:
120
+ await self._session.close()
121
+ self._session = None
122
+ self._agent_card = None
123
+
124
+ @property
125
+ def agent_card(self) -> Optional[AgentCard]:
126
+ """Get the cached agent card."""
127
+ return self._agent_card
128
+
129
+ @property
130
+ def is_connected(self) -> bool:
131
+ return self._session is not None and not self._session.closed
132
+
133
+ # ─────────────────────────────────────────────────────────────
134
+ # Discovery
135
+ # ─────────────────────────────────────────────────────────────
136
+
137
+ async def discover(self) -> AgentCard:
138
+ """Fetch the remote agent's card."""
139
+ url = f"{self.base_url}/.well-known/agent.json"
140
+
141
+ async with self._session.get(url) as resp:
142
+ resp.raise_for_status()
143
+ data = await resp.json()
144
+
145
+ # Parse into AgentCard
146
+ skills = [
147
+ AgentSkill(
148
+ id=s.get("id", ""),
149
+ name=s.get("name", ""),
150
+ description=s.get("description", ""),
151
+ tags=s.get("tags", []),
152
+ input_schema=s.get("inputSchema"),
153
+ )
154
+ for s in data.get("skills", [])
155
+ ]
156
+
157
+ return AgentCard(
158
+ name=data.get("name", "Unknown"),
159
+ description=data.get("description", ""),
160
+ version=data.get("version", "1.0.0"),
161
+ url=data.get("url") or self.base_url,
162
+ skills=skills,
163
+ protocol_version=data.get("protocolVersion", "0.3"),
164
+ )
165
+
166
+ def get_skills(self) -> List[AgentSkill]:
167
+ """Get available skills from the remote agent."""
168
+ if not self._agent_card:
169
+ return []
170
+ return self._agent_card.skills
171
+
172
+ def get_skill(self, skill_id: str) -> Optional[AgentSkill]:
173
+ """Get a specific skill by ID."""
174
+ for skill in self.get_skills():
175
+ if skill.id == skill_id:
176
+ return skill
177
+ return None
178
+
179
+ # ─────────────────────────────────────────────────────────────
180
+ # Messaging
181
+ # ─────────────────────────────────────────────────────────────
182
+
183
+ async def send_message(
184
+ self,
185
+ content: str,
186
+ *,
187
+ context_id: Optional[str] = None,
188
+ metadata: Optional[Dict[str, Any]] = None,
189
+ ) -> Task:
190
+ """
191
+ Send a message to the remote agent and wait for response.
192
+
193
+ Args:
194
+ content: The message content
195
+ context_id: Optional context ID for multi-turn conversations
196
+ metadata: Optional metadata to include
197
+
198
+ Returns:
199
+ Task with the response
200
+ """
201
+ url = f"{self.base_url}/a2a/message/send"
202
+
203
+ message = Message.user(content, context_id=context_id, metadata=metadata)
204
+
205
+ payload = {
206
+ "message": message.to_dict()
207
+ }
208
+
209
+ async with self._session.post(url, json=payload) as resp:
210
+ resp.raise_for_status()
211
+ data = await resp.json()
212
+
213
+ return self._parse_task(data)
214
+
215
+ async def stream_message(
216
+ self,
217
+ content: str,
218
+ *,
219
+ context_id: Optional[str] = None,
220
+ metadata: Optional[Dict[str, Any]] = None,
221
+ ) -> AsyncIterator[str]:
222
+ """
223
+ Send a message and stream the response.
224
+
225
+ Args:
226
+ content: The message content
227
+ context_id: Optional context ID
228
+ metadata: Optional metadata
229
+
230
+ Yields:
231
+ Text chunks as they arrive
232
+ """
233
+ url = f"{self.base_url}/a2a/message/stream"
234
+
235
+ message = Message.user(content, context_id=context_id, metadata=metadata)
236
+
237
+ payload = {
238
+ "message": message.to_dict()
239
+ }
240
+
241
+ async with self._session.post(
242
+ url,
243
+ json=payload,
244
+ headers={"Accept": "text/event-stream"}
245
+ ) as resp:
246
+ resp.raise_for_status()
247
+
248
+ async for line in resp.content:
249
+ line = line.decode("utf-8").strip()
250
+
251
+ if not line or not line.startswith("data:"):
252
+ continue
253
+
254
+ try:
255
+ data = json.loads(line[5:].strip())
256
+
257
+ # Extract text from artifact updates
258
+ if "artifactUpdate" in data:
259
+ artifact = data["artifactUpdate"].get("artifact", {})
260
+ parts = artifact.get("parts", [])
261
+ for part in parts:
262
+ if "text" in part:
263
+ yield part["text"]
264
+
265
+ # Check for completion/failure
266
+ if "statusUpdate" in data:
267
+ status = data["statusUpdate"]
268
+ if status.get("final"):
269
+ state = status.get("status", {}).get("state")
270
+ if state == "failed":
271
+ error_msg = status.get("status", {}).get("message", {})
272
+ error_text = error_msg.get("parts", [{}])[0].get("text", "Unknown error")
273
+ raise RuntimeError(f"Remote agent failed: {error_text}")
274
+ break
275
+
276
+ except json.JSONDecodeError:
277
+ continue
278
+
279
+ async def invoke_skill(
280
+ self,
281
+ skill_id: str,
282
+ params: Optional[Dict[str, Any]] = None,
283
+ *,
284
+ context_id: Optional[str] = None,
285
+ ) -> Any:
286
+ """
287
+ Invoke a specific skill on the remote agent.
288
+
289
+ Args:
290
+ skill_id: The skill ID to invoke
291
+ params: Parameters to pass to the skill
292
+ context_id: Optional context ID
293
+
294
+ Returns:
295
+ The skill result (extracted from artifacts)
296
+ """
297
+ message = Message.user(
298
+ {"skill": skill_id, "params": params or {}},
299
+ context_id=context_id
300
+ )
301
+
302
+ url = f"{self.base_url}/a2a/message/send"
303
+ payload = {"message": message.to_dict()}
304
+
305
+ async with self._session.post(url, json=payload) as resp:
306
+ resp.raise_for_status()
307
+ data = await resp.json()
308
+
309
+ task = self._parse_task(data)
310
+
311
+ if task.status.state == TaskState.FAILED:
312
+ error_msg = task.status.message.get_text() if task.status.message else "Unknown error"
313
+ raise RuntimeError(f"Skill invocation failed: {error_msg}")
314
+
315
+ # Extract result from artifacts
316
+ if task.artifacts:
317
+ artifact = task.artifacts[0]
318
+ if artifact.parts:
319
+ part = artifact.parts[0]
320
+ if part.data:
321
+ return part.data
322
+ return part.text
323
+
324
+ return None
325
+
326
+ # ─────────────────────────────────────────────────────────────
327
+ # Task Management
328
+ # ─────────────────────────────────────────────────────────────
329
+
330
+ async def get_task(self, task_id: str) -> Task:
331
+ """Get a task by ID."""
332
+ url = f"{self.base_url}/a2a/tasks/{task_id}"
333
+
334
+ async with self._session.get(url) as resp:
335
+ resp.raise_for_status()
336
+ data = await resp.json()
337
+
338
+ return self._parse_task(data)
339
+
340
+ async def list_tasks(
341
+ self,
342
+ context_id: Optional[str] = None,
343
+ status: Optional[str] = None,
344
+ page_size: int = 50,
345
+ ) -> List[Task]:
346
+ """List tasks with optional filtering."""
347
+ url = f"{self.base_url}/a2a/tasks"
348
+ params = {"pageSize": page_size}
349
+ if context_id:
350
+ params["contextId"] = context_id
351
+ if status:
352
+ params["status"] = status
353
+
354
+ async with self._session.get(url, params=params) as resp:
355
+ resp.raise_for_status()
356
+ data = await resp.json()
357
+
358
+ return [self._parse_task(t) for t in data.get("tasks", [])]
359
+
360
+ async def cancel_task(self, task_id: str) -> Task:
361
+ """Cancel a running task."""
362
+ url = f"{self.base_url}/a2a/tasks/{task_id}/cancel"
363
+
364
+ async with self._session.post(url) as resp:
365
+ resp.raise_for_status()
366
+ data = await resp.json()
367
+
368
+ return self._parse_task(data)
369
+
370
+ # ─────────────────────────────────────────────────────────────
371
+ # JSON-RPC
372
+ # ─────────────────────────────────────────────────────────────
373
+
374
+ async def rpc_call(
375
+ self,
376
+ method: str,
377
+ params: Optional[Dict[str, Any]] = None,
378
+ ) -> Any:
379
+ """Make a JSON-RPC call to the remote agent."""
380
+ url = f"{self.base_url}/a2a/rpc"
381
+
382
+ payload = {
383
+ "jsonrpc": "2.0",
384
+ "id": str(id(self)),
385
+ "method": method,
386
+ "params": params or {}
387
+ }
388
+
389
+ async with self._session.post(url, json=payload) as resp:
390
+ resp.raise_for_status()
391
+ data = await resp.json()
392
+
393
+ if "error" in data:
394
+ raise RuntimeError(f"RPC error: {data['error']}")
395
+
396
+ return data.get("result")
397
+
398
+ # ─────────────────────────────────────────────────────────────
399
+ # Helpers
400
+ # ─────────────────────────────────────────────────────────────
401
+
402
+ def _parse_task(self, data: Dict[str, Any]) -> Task:
403
+ """Parse a task from JSON response."""
404
+ status_data = data.get("status", {})
405
+ status = TaskStatus(
406
+ state=TaskState(status_data.get("state", "submitted")),
407
+ message=Message.from_dict(status_data["message"]) if status_data.get("message") else None,
408
+ timestamp=status_data.get("timestamp"),
409
+ )
410
+
411
+ artifacts = []
412
+ for a in data.get("artifacts", []):
413
+ parts = [Part.from_dict(p) for p in a.get("parts", [])]
414
+ artifacts.append(Artifact(
415
+ artifact_id=a.get("artifactId", ""),
416
+ name=a.get("name"),
417
+ description=a.get("description"),
418
+ parts=parts,
419
+ metadata=a.get("metadata"),
420
+ ))
421
+
422
+ history = [Message.from_dict(m) for m in data.get("history", [])]
423
+
424
+ return Task(
425
+ id=data.get("id", ""),
426
+ context_id=data.get("contextId", ""),
427
+ status=status,
428
+ artifacts=artifacts,
429
+ history=history,
430
+ metadata=data.get("metadata"),
431
+ )
432
+
433
+
434
+ # ─────────────────────────────────────────────────────────────
435
+ # A2A Remote Tools - AbstractTool Implementations
436
+ # ─────────────────────────────────────────────────────────────
437
+
438
+
439
+ class A2ARemoteAgentInput(AbstractToolArgsSchema):
440
+ """Input schema for A2A remote agent tool."""
441
+ question: str = Field(
442
+ ...,
443
+ description="The question to ask the remote A2A agent"
444
+ )
445
+ context_id: Optional[str] = Field(
446
+ None,
447
+ description="Optional context ID for multi-turn conversations"
448
+ )
449
+
450
+
451
+ class A2ARemoteAgentTool(AbstractTool):
452
+ """
453
+ Wraps a remote A2A agent as a tool that can be used by local agents.
454
+
455
+ This creates a tool that, when invoked, sends the query to the remote agent.
456
+ Properly inherits from AbstractTool for ToolManager compatibility.
457
+ """
458
+
459
+ name: str = "ask_remote_agent"
460
+ description: str = "Ask a question to a remote A2A agent"
461
+ args_schema: Type[BaseModel] = A2ARemoteAgentInput
462
+
463
+ def __init__(
464
+ self,
465
+ client: A2AClient,
466
+ *,
467
+ tool_name: Optional[str] = None,
468
+ tool_description: Optional[str] = None,
469
+ use_streaming: bool = False,
470
+ **kwargs
471
+ ):
472
+ """
473
+ Initialize A2A Remote Agent Tool.
474
+
475
+ Args:
476
+ client: Connected A2AClient instance
477
+ tool_name: Custom name for the tool (defaults to ask_<agent_name>)
478
+ tool_description: Custom description (defaults to agent card description)
479
+ use_streaming: Whether to use streaming for responses
480
+ """
481
+ self.client = client
482
+ self.use_streaming = use_streaming
483
+ self.tags = ["a2a", "remote-agent"]
484
+
485
+ card = client.agent_card
486
+ name = tool_name or f"ask_{card.name.lower().replace(' ', '_')}"
487
+ description = tool_description or (
488
+ f"Ask the remote agent '{card.name}': {card.description}"
489
+ )
490
+
491
+ # Store for cloning
492
+ self._tool_name_override = tool_name
493
+ self._tool_description_override = tool_description
494
+
495
+ super().__init__(name=name, description=description, **kwargs)
496
+
497
+ def _get_clone_kwargs(self) -> Dict[str, Any]:
498
+ """Get kwargs for cloning this tool."""
499
+ base_kwargs = super()._get_clone_kwargs()
500
+ base_kwargs.update({
501
+ 'tool_name': self._tool_name_override,
502
+ 'tool_description': self._tool_description_override,
503
+ 'use_streaming': self.use_streaming,
504
+ })
505
+ return base_kwargs
506
+
507
+ def clone(self) -> "A2ARemoteAgentTool":
508
+ """Clone this tool (shares the client reference)."""
509
+ clone_kwargs = self._get_clone_kwargs()
510
+ # Remove standard AbstractTool params that we handle differently
511
+ clone_kwargs.pop('name', None)
512
+ clone_kwargs.pop('description', None)
513
+ return A2ARemoteAgentTool(client=self.client, **clone_kwargs)
514
+
515
+ async def _execute(self, question: str, context_id: Optional[str] = None, **kwargs) -> str:
516
+ """Execute the tool by sending a message to the remote agent."""
517
+ # Also support session_id as an alias
518
+ ctx_id = context_id or kwargs.get("session_id")
519
+
520
+ if self.use_streaming:
521
+ chunks = []
522
+ async for chunk in self.client.stream_message(question, context_id=ctx_id):
523
+ chunks.append(chunk)
524
+ return "".join(chunks)
525
+ else:
526
+ task = await self.client.send_message(question, context_id=ctx_id)
527
+
528
+ if task.status.state == TaskState.FAILED:
529
+ error = task.status.message.get_text() if task.status.message else "Unknown error"
530
+ return f"Error from remote agent: {error}"
531
+
532
+ if task.artifacts:
533
+ return task.artifacts[0].parts[0].text if task.artifacts[0].parts else ""
534
+
535
+ return ""
536
+
537
+
538
+ def _create_skill_input_model(skill: AgentSkill) -> Type[AbstractToolArgsSchema]:
539
+ """
540
+ Dynamically create a Pydantic model from an AgentSkill's input_schema.
541
+
542
+ Args:
543
+ skill: The AgentSkill with optional input_schema
544
+
545
+ Returns:
546
+ A Pydantic model class for the skill's inputs
547
+ """
548
+ # Base fields that are always available
549
+ field_definitions: Dict[str, Any] = {
550
+ 'context_id': (
551
+ Optional[str],
552
+ Field(None, description="Optional context ID for multi-turn conversations")
553
+ ),
554
+ }
555
+
556
+ # Parse input_schema if available
557
+ if skill.input_schema and isinstance(skill.input_schema, dict):
558
+ properties = skill.input_schema.get('properties', {})
559
+ required = skill.input_schema.get('required', [])
560
+
561
+ for prop_name, prop_info in properties.items():
562
+ if prop_name == 'context_id':
563
+ continue # Already handled
564
+
565
+ prop_type = prop_info.get('type', 'string')
566
+ prop_desc = prop_info.get('description', f"Parameter: {prop_name}")
567
+
568
+ # Map JSON schema types to Python types
569
+ type_mapping = {
570
+ 'string': str,
571
+ 'integer': int,
572
+ 'number': float,
573
+ 'boolean': bool,
574
+ 'array': list,
575
+ 'object': dict,
576
+ }
577
+ python_type = type_mapping.get(prop_type, str)
578
+
579
+ if prop_name in required:
580
+ field_definitions[prop_name] = (
581
+ python_type,
582
+ Field(..., description=prop_desc)
583
+ )
584
+ else:
585
+ field_definitions[prop_name] = (
586
+ Optional[python_type],
587
+ Field(None, description=prop_desc)
588
+ )
589
+
590
+ # Create dynamic model
591
+ model_name = f"{skill.name.replace(' ', '')}Input"
592
+ # Use create_model from pydantic if available, otherwise use type
593
+ try:
594
+ from pydantic import create_model
595
+ return create_model(model_name, __base__=AbstractToolArgsSchema, **field_definitions)
596
+ except ImportError:
597
+ # Fallback: create a simple class
598
+ return type(model_name, (AbstractToolArgsSchema,), {
599
+ '__annotations__': {k: v[0] for k, v in field_definitions.items()}
600
+ })
601
+
602
+
603
+ class A2ARemoteSkillTool(AbstractTool):
604
+ """
605
+ Wraps a specific skill from a remote A2A agent as a tool.
606
+
607
+ Properly inherits from AbstractTool for ToolManager compatibility.
608
+ Dynamically generates input schema from the skill's input_schema.
609
+ """
610
+
611
+ name: str = "remote_skill"
612
+ description: str = "Invoke a remote skill"
613
+ args_schema: Type[BaseModel] = AbstractToolArgsSchema
614
+
615
+ def __init__(
616
+ self,
617
+ client: A2AClient,
618
+ skill: AgentSkill,
619
+ **kwargs
620
+ ):
621
+ """
622
+ Initialize A2A Remote Skill Tool.
623
+
624
+ Args:
625
+ client: Connected A2AClient instance
626
+ skill: The AgentSkill to wrap as a tool
627
+ """
628
+ self.client = client
629
+ self.skill = skill
630
+ self.tags = ["a2a", "remote-skill"] + skill.tags
631
+
632
+ name = f"remote_{skill.id}"
633
+ description = skill.description or f"Invoke remote skill: {skill.name}"
634
+
635
+ # Dynamically create the args schema from skill.input_schema
636
+ self.args_schema = _create_skill_input_model(skill)
637
+
638
+ super().__init__(name=name, description=description, **kwargs)
639
+
640
+ def _get_clone_kwargs(self) -> Dict[str, Any]:
641
+ """Get kwargs for cloning this tool."""
642
+ base_kwargs = super()._get_clone_kwargs()
643
+ # Remove AbstractTool params we handle in __init__
644
+ base_kwargs.pop('name', None)
645
+ base_kwargs.pop('description', None)
646
+ return base_kwargs
647
+
648
+ def clone(self) -> "A2ARemoteSkillTool":
649
+ """Clone this tool (shares the client and skill references)."""
650
+ clone_kwargs = self._get_clone_kwargs()
651
+ return A2ARemoteSkillTool(client=self.client, skill=self.skill, **clone_kwargs)
652
+
653
+ async def _execute(self, context_id: Optional[str] = None, **kwargs) -> Any:
654
+ """Execute the remote skill."""
655
+ # Also support session_id as an alias
656
+ ctx_id = context_id or kwargs.pop("session_id", None)
657
+ return await self.client.invoke_skill(self.skill.id, kwargs, context_id=ctx_id)
658
+