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,988 @@
1
+ """
2
+ AWS CloudWatch Tool for AI-Parrot
3
+ Enables AI agents to query CloudWatch logs and metrics
4
+ """
5
+ from typing import Optional, List, Dict, Any, Literal
6
+ from datetime import datetime, timedelta, timezone
7
+ from enum import Enum
8
+ import contextlib
9
+ import re
10
+ import asyncio
11
+ from pydantic import Field, field_validator
12
+ from botocore.exceptions import ClientError
13
+ from ..interfaces.aws import AWSInterface
14
+ from ..conf import AWS_DEFAULT_CLOUDWATCH_LOG_GROUP
15
+ from .abstract import AbstractTool, AbstractToolArgsSchema, ToolResult
16
+
17
+
18
+ class CloudWatchOperation(str, Enum):
19
+ """Available CloudWatch operations"""
20
+ QUERY_LOGS = "query_logs"
21
+ GET_METRICS = "get_metrics"
22
+ LIST_LOG_GROUPS = "list_log_groups"
23
+ LIST_LOG_STREAMS = "list_log_streams"
24
+ GET_LOG_EVENTS = "get_log_events"
25
+ PUT_METRIC_DATA = "put_metric_data"
26
+ DESCRIBE_ALARMS = "describe_alarms"
27
+ LOG_SUMMARY = "log_summary"
28
+
29
+
30
+ class CloudWatchToolArgs(AbstractToolArgsSchema):
31
+ """Arguments schema for CloudWatch operations"""
32
+
33
+ operation: CloudWatchOperation = Field(
34
+ ...,
35
+ description=(
36
+ "CloudWatch operation to perform:\n"
37
+ "- 'query_logs': Run CloudWatch Logs Insights query\n"
38
+ "- 'get_metrics': Get metric statistics\n"
39
+ "- 'list_log_groups': List available log groups\n"
40
+ "- 'list_log_streams': List log streams in a log group\n"
41
+ "- 'get_log_events': Get recent log events from a stream\n"
42
+ "- 'put_metric_data': Publish custom metric data\n"
43
+ "- 'describe_alarms': List CloudWatch alarms\n"
44
+ "- 'log_summary': Get summarized log events with parsed facility, time, and truncated messages"
45
+ )
46
+ )
47
+
48
+ # Log query parameters
49
+ log_group_name: Optional[str] = Field(
50
+ None,
51
+ description="CloudWatch log group name (e.g., '/aws/lambda/my-function')"
52
+ )
53
+
54
+ query_string: Optional[str] = Field(
55
+ None,
56
+ description=(
57
+ "CloudWatch Logs Insights query. Examples:\n"
58
+ "- 'fields @timestamp, @message | sort @timestamp desc | limit 20'\n"
59
+ "- 'filter @message like /ERROR/ | stats count() by bin(5m)'"
60
+ )
61
+ )
62
+
63
+ log_stream_name: Optional[str] = Field(
64
+ None,
65
+ description="Specific log stream name within the log group"
66
+ )
67
+
68
+ # Time range parameters
69
+ start_time: Optional[str] = Field(
70
+ None,
71
+ description=(
72
+ "Start time for query (ISO format or relative like '-1h', '-30m', '-7d'). "
73
+ "Examples: '2024-01-01T00:00:00', '-1h', '-24h'"
74
+ )
75
+ )
76
+
77
+ end_time: Optional[str] = Field(
78
+ None,
79
+ description="End time for query (ISO format or 'now'). Default: now"
80
+ )
81
+
82
+ # Metric parameters
83
+ namespace: Optional[str] = Field(
84
+ None,
85
+ description="CloudWatch metric namespace (e.g., 'AWS/Lambda', 'AWS/EC2', custom namespace)"
86
+ )
87
+
88
+ metric_name: Optional[str] = Field(
89
+ None,
90
+ description="Metric name to query (e.g., 'Duration', 'Invocations', 'CPUUtilization')"
91
+ )
92
+
93
+ dimensions: Optional[List[Dict[str, str]]] = Field(
94
+ None,
95
+ description=(
96
+ "Metric dimensions as list of {Name: ..., Value: ...} dicts. "
97
+ "Example: [{'Name': 'FunctionName', 'Value': 'my-function'}]"
98
+ )
99
+ )
100
+
101
+ statistic: Optional[Literal["Average", "Sum", "Minimum", "Maximum", "SampleCount"]] = Field(
102
+ "Average",
103
+ description="Statistic to retrieve for metrics"
104
+ )
105
+
106
+ period: Optional[int] = Field(
107
+ 60,
108
+ description="Period in seconds for metric data points (60, 300, 3600, etc.)"
109
+ )
110
+
111
+ # General parameters
112
+ limit: Optional[int] = Field(
113
+ 50,
114
+ description="Maximum number of results to return"
115
+ )
116
+
117
+ pattern: Optional[str] = Field(
118
+ None,
119
+ description="Filter pattern for log groups/streams (supports wildcards)"
120
+ )
121
+
122
+ # Custom metric publishing
123
+ metric_value: Optional[float] = Field(
124
+ None,
125
+ description="Metric value to publish (for put_metric_data operation)"
126
+ )
127
+
128
+ unit: Optional[str] = Field(
129
+ None,
130
+ description="Metric unit (e.g., 'Seconds', 'Count', 'Bytes')"
131
+ )
132
+
133
+ max_message_length: Optional[int] = Field(
134
+ 500,
135
+ description="Maximum length for log messages in log_summary operation (default: 500 characters)"
136
+ )
137
+
138
+ @field_validator('start_time', mode='before')
139
+ @classmethod
140
+ def parse_time(cls, v):
141
+ """Parse time string to timestamp"""
142
+ if v is None or v == 'now':
143
+ return v
144
+ return v # Will be parsed in the tool
145
+
146
+ @field_validator('end_time', mode='before')
147
+ @classmethod
148
+ def parse_end_time(cls, v):
149
+ """Parse end time string to timestamp"""
150
+ if v is None or v == 'now':
151
+ return v
152
+ return v # Will be parsed in the tool
153
+
154
+
155
+ class CloudWatchTool(AbstractTool):
156
+ """
157
+ Tool for querying AWS CloudWatch logs and metrics.
158
+
159
+ Capabilities:
160
+ - Query logs using CloudWatch Logs Insights
161
+ - Retrieve metric statistics and timeseries data
162
+ - List and explore log groups and streams
163
+ - Get recent log events
164
+ - Get summarized log events with parsed facility and truncated messages
165
+ - Publish custom metrics
166
+ - Check alarm status
167
+
168
+ Example Usage:
169
+ # Query logs for errors in last hour
170
+ {
171
+ "operation": "query_logs",
172
+ "log_group_name": "/aws/lambda/my-function",
173
+ "query_string": "fields @timestamp, @message | filter @message like /ERROR/ | limit 50",
174
+ "start_time": "-1h"
175
+ }
176
+
177
+ # Get Lambda duration metrics
178
+ {
179
+ "operation": "get_metrics",
180
+ "namespace": "AWS/Lambda",
181
+ "metric_name": "Duration",
182
+ "dimensions": [{"Name": "FunctionName", "Value": "my-function"}],
183
+ "statistic": "Average",
184
+ "start_time": "-24h"
185
+ }
186
+
187
+ # Get summarized logs with truncated messages
188
+ {
189
+ "operation": "log_summary",
190
+ "log_group_name": "/aws/lambda/my-function",
191
+ "limit": 50,
192
+ "max_message_length": 200,
193
+ "start_time": "-1h"
194
+ }
195
+ """
196
+
197
+ name: str = "cloudwatch"
198
+ description: str = (
199
+ "Query AWS CloudWatch logs and metrics. "
200
+ "Supports log queries, metric retrieval, and monitoring operations."
201
+ )
202
+ args_schema: type[AbstractToolArgsSchema] = CloudWatchToolArgs
203
+
204
+ def __init__(
205
+ self,
206
+ aws_id: str = 'cloudwatch',
207
+ region_name: Optional[str] = None,
208
+ default_log_group: Optional[str] = None,
209
+ max_query_wait: int = 30,
210
+ **kwargs
211
+ ):
212
+ """
213
+ Initialize CloudWatch tool.
214
+
215
+ Args:
216
+ aws_id: AWS credentials identifier
217
+ region_name: AWS region
218
+ default_log_group: Default log group for queries
219
+ max_query_wait: Maximum seconds to wait for Insights query completion
220
+ **kwargs: Additional AWS interface parameters
221
+ """
222
+ super().__init__()
223
+ self.aws = AWSInterface(
224
+ aws_id=aws_id,
225
+ region_name=region_name,
226
+ **kwargs
227
+ )
228
+ self.default_log_group = default_log_group or AWS_DEFAULT_CLOUDWATCH_LOG_GROUP
229
+ self.max_query_wait = max_query_wait
230
+
231
+ def _parse_relative_time(self, time_str: str) -> datetime:
232
+ """
233
+ Parse relative time strings like '-1h', '-30m', '-7d'.
234
+
235
+ Args:
236
+ time_str: Time string (e.g., '-1h', '-24h', '-7d')
237
+
238
+ Returns:
239
+ datetime object
240
+ """
241
+ if time_str == 'now' or time_str is None:
242
+ return datetime.now(timezone.utc)
243
+
244
+ # Try parsing as ISO format first
245
+ with contextlib.suppress(ValueError, AttributeError):
246
+ return datetime.fromisoformat(time_str.replace('Z', '+00:00'))
247
+
248
+ # Parse relative time
249
+ if time_str.startswith('-'):
250
+ time_str = time_str[1:]
251
+
252
+ # Extract number and unit
253
+ match = re.match(r'(\d+)([smhd])', time_str)
254
+ if not match:
255
+ raise ValueError(f"Invalid time format: {time_str}")
256
+
257
+ amount, unit = match.groups()
258
+ amount = int(amount)
259
+
260
+ if unit == 's':
261
+ delta = timedelta(seconds=amount)
262
+ elif unit == 'm':
263
+ delta = timedelta(minutes=amount)
264
+ elif unit == 'h':
265
+ delta = timedelta(hours=amount)
266
+ elif unit == 'd':
267
+ delta = timedelta(days=amount)
268
+ else:
269
+ raise ValueError(f"Unknown time unit: {unit}")
270
+
271
+ return datetime.now(timezone.utc) - delta
272
+
273
+ raise ValueError(f"Invalid time format: {time_str}")
274
+
275
+ def _parse_log_message(self, message: str, timestamp: str) -> Dict[str, Any]:
276
+ """
277
+ Parse log message to extract facility, time, and message.
278
+
279
+ Supports multiple log formats:
280
+ - Rails/Ruby logger: I, [timestamp#pid] LEVEL -- : message
281
+ - Standard syslog: LEVEL: message
282
+ - Plain messages
283
+
284
+ Args:
285
+ message: Raw log message
286
+ timestamp: ISO timestamp of the log event
287
+
288
+ Returns:
289
+ Dict with 'facility', 'timestamp', and 'message' keys
290
+ """
291
+ # Default values
292
+ facility = "INFO"
293
+ parsed_message = message
294
+
295
+ # Try to parse Rails/Ruby logger format
296
+ # Example: I, [2025-12-03T00:17:10.802698#1-142140] INFO -- : Running job...
297
+ rails_pattern = r'^([A-Z]),\s*\[([^\]]+)\]\s*(\w+)\s*--\s*:\s*(.+)$'
298
+ match = re.match(rails_pattern, message)
299
+ if match:
300
+ level_code, log_timestamp, level_name, msg = match.groups()
301
+ facility = level_name
302
+ parsed_message = msg.strip()
303
+ return {
304
+ 'facility': facility,
305
+ 'timestamp': timestamp,
306
+ 'message': parsed_message
307
+ }
308
+
309
+ # Try to parse standard log format with level prefix
310
+ # Example: ERROR: Something went wrong
311
+ # Example: [ERROR] Something went wrong
312
+ # Example: 2025-12-03 ERROR: Something went wrong
313
+ level_patterns = [
314
+ r'^\[?(DEBUG|INFO|WARN|WARNING|ERROR|FATAL|CRITICAL)\]?\s*:?\s*(.+)$',
315
+ r'^\d{4}-\d{2}-\d{2}[T\s]\d{2}:\d{2}:\d{2}[^\s]*\s+\[?(DEBUG|INFO|WARN|WARNING|ERROR|FATAL|CRITICAL)\]?\s*:?\s*(.+)$'
316
+ ]
317
+
318
+ for pattern in level_patterns:
319
+ match = re.match(pattern, message, re.IGNORECASE)
320
+ if match:
321
+ groups = match.groups()
322
+ if len(groups) == 2:
323
+ facility = groups[0].upper()
324
+ parsed_message = groups[1].strip()
325
+ else: # Has timestamp in message
326
+ facility = groups[1].upper()
327
+ parsed_message = groups[2].strip()
328
+ break
329
+
330
+ # Clean up common log artifacts
331
+ parsed_message = parsed_message.replace('\\n', ' ').replace('\\t', ' ')
332
+ parsed_message = re.sub(r'\s+', ' ', parsed_message).strip()
333
+
334
+ return {
335
+ 'facility': facility,
336
+ 'timestamp': timestamp,
337
+ 'message': parsed_message
338
+ }
339
+
340
+ def _truncate_message(self, message: str, max_length: int) -> str:
341
+ """
342
+ Truncate message to max_length, adding ellipsis if truncated.
343
+
344
+ Args:
345
+ message: Message to truncate
346
+ max_length: Maximum length
347
+
348
+ Returns:
349
+ Truncated message
350
+ """
351
+ if len(message) <= max_length:
352
+ return message
353
+ return message[:max_length - 3] + "..."
354
+
355
+ async def _log_summary(
356
+ self,
357
+ log_group_name: str,
358
+ log_stream_name: Optional[str] = None,
359
+ start_time: Optional[datetime] = None,
360
+ limit: int = 100,
361
+ max_message_length: int = 500
362
+ ) -> List[Dict[str, Any]]:
363
+ """
364
+ Get summarized log events with parsed facility, time, and truncated messages.
365
+
366
+ Args:
367
+ log_group_name: CloudWatch log group name
368
+ log_stream_name: Optional specific log stream
369
+ start_time: Optional start time for filtering
370
+ limit: Maximum number of log events
371
+ max_message_length: Maximum length for messages
372
+
373
+ Returns:
374
+ List of summarized log events
375
+ """
376
+ # Get log events
377
+ if log_stream_name:
378
+ events = await self._get_log_events(
379
+ log_group_name=log_group_name,
380
+ log_stream_name=log_stream_name,
381
+ start_time=start_time,
382
+ limit=limit
383
+ )
384
+ else:
385
+ # If no stream specified, get events from the most recent stream
386
+ streams = await self._list_log_streams(
387
+ log_group_name=log_group_name,
388
+ limit=1
389
+ )
390
+ if not streams:
391
+ return []
392
+
393
+ events = await self._get_log_events(
394
+ log_group_name=log_group_name,
395
+ log_stream_name=streams[0]['name'],
396
+ start_time=start_time,
397
+ limit=limit
398
+ )
399
+
400
+ # Parse and summarize each event
401
+ summarized_events = []
402
+ for event in events:
403
+ parsed = self._parse_log_message(
404
+ event['message'],
405
+ event['timestamp']
406
+ )
407
+
408
+ summarized_events.append({
409
+ 'timestamp': parsed['timestamp'],
410
+ 'facility': parsed['facility'],
411
+ 'message': self._truncate_message(
412
+ parsed['message'],
413
+ max_message_length
414
+ )
415
+ })
416
+
417
+ return summarized_events
418
+
419
+ async def _query_logs_insights(
420
+ self,
421
+ log_group_name: str,
422
+ query_string: str,
423
+ start_time: datetime,
424
+ end_time: datetime,
425
+ limit: int
426
+ ) -> List[Dict[str, Any]]:
427
+ """Execute CloudWatch Logs Insights query"""
428
+
429
+ async with self.aws.client('logs') as logs:
430
+ # Start query
431
+ response = await logs.start_query(
432
+ logGroupName=log_group_name,
433
+ startTime=int(start_time.timestamp()),
434
+ endTime=int(end_time.timestamp()),
435
+ queryString=query_string,
436
+ limit=limit
437
+ )
438
+
439
+ query_id = response['queryId']
440
+
441
+ # Poll for results
442
+ for _ in range(self.max_query_wait):
443
+ result = await logs.get_query_results(queryId=query_id)
444
+
445
+ status = result['status']
446
+
447
+ if status == 'Complete':
448
+ # Parse results
449
+ parsed_results = []
450
+ for record in result.get('results', []):
451
+ parsed_record = {field['field']: field['value'] for field in record}
452
+ parsed_results.append(parsed_record)
453
+
454
+ return parsed_results
455
+
456
+ elif status in ['Failed', 'Cancelled']:
457
+ raise RuntimeError(f"Query failed with status: {status}")
458
+
459
+ await asyncio.sleep(1)
460
+
461
+ raise TimeoutError(f"Query did not complete within {self.max_query_wait} seconds")
462
+
463
+ async def _get_metrics(
464
+ self,
465
+ namespace: str,
466
+ metric_name: str,
467
+ dimensions: List[Dict[str, str]],
468
+ statistic: str,
469
+ start_time: datetime,
470
+ end_time: datetime,
471
+ period: int
472
+ ) -> Dict[str, Any]:
473
+ """Get metric statistics"""
474
+
475
+ async with self.aws.client('cloudwatch') as cloudwatch:
476
+ # Convert dimensions to proper format
477
+ dims = [
478
+ {'Name': d['Name'], 'Value': d['Value']}
479
+ for d in (dimensions or [])
480
+ ]
481
+
482
+ response = await cloudwatch.get_metric_statistics(
483
+ Namespace=namespace,
484
+ MetricName=metric_name,
485
+ Dimensions=dims,
486
+ StartTime=start_time,
487
+ EndTime=end_time,
488
+ Period=period,
489
+ Statistics=[statistic]
490
+ )
491
+
492
+ # Sort datapoints by timestamp
493
+ datapoints = sorted(
494
+ response.get('Datapoints', []),
495
+ key=lambda x: x['Timestamp']
496
+ )
497
+
498
+ return {
499
+ 'label': response.get('Label', metric_name),
500
+ 'datapoints': [
501
+ {
502
+ 'timestamp': dp['Timestamp'].isoformat(),
503
+ 'value': dp.get(statistic),
504
+ 'unit': dp.get('Unit')
505
+ }
506
+ for dp in datapoints
507
+ ]
508
+ }
509
+
510
+ async def _list_log_groups(
511
+ self,
512
+ pattern: Optional[str] = None,
513
+ limit: int = 50
514
+ ) -> List[Dict[str, Any]]:
515
+ """List CloudWatch log groups"""
516
+
517
+ async with self.aws.client('logs') as logs:
518
+ params: Dict[str, Any] = {}
519
+ if pattern:
520
+ params['logGroupNamePrefix'] = pattern
521
+
522
+ log_groups: List[Dict[str, Any]] = []
523
+ paginator = logs.get_paginator('describe_log_groups')
524
+
525
+ async for page in paginator.paginate(**params):
526
+ for lg in page.get('logGroups', []):
527
+ log_groups.append({
528
+ 'name': lg['logGroupName'],
529
+ 'creation_time': datetime.fromtimestamp(
530
+ lg['creationTime'] / 1000
531
+ ).isoformat(),
532
+ 'stored_bytes': lg.get('storedBytes', 0),
533
+ 'retention_days': lg.get('retentionInDays')
534
+ })
535
+
536
+ if len(log_groups) >= limit:
537
+ break
538
+
539
+ return log_groups[:limit]
540
+
541
+ async def _list_log_streams(
542
+ self,
543
+ log_group_name: str,
544
+ limit: int = 50
545
+ ) -> List[Dict[str, Any]]:
546
+ """List log streams in a log group"""
547
+
548
+ async with self.aws.client('logs') as logs:
549
+ response = await logs.describe_log_streams(
550
+ logGroupName=log_group_name,
551
+ orderBy='LastEventTime',
552
+ descending=True,
553
+ limit=limit
554
+ )
555
+
556
+ return [
557
+ {
558
+ 'name': ls['logStreamName'],
559
+ 'creation_time': datetime.fromtimestamp(
560
+ ls['creationTime'] / 1000
561
+ ).isoformat(),
562
+ 'last_event_time': datetime.fromtimestamp(
563
+ ls.get('lastEventTimestamp', ls['creationTime']) / 1000
564
+ ).isoformat() if ls.get('lastEventTimestamp') else None,
565
+ 'stored_bytes': ls.get('storedBytes', 0)
566
+ }
567
+ for ls in response.get('logStreams', [])
568
+ ]
569
+
570
+ async def _get_log_events(
571
+ self,
572
+ log_group_name: str,
573
+ log_stream_name: str,
574
+ start_time: Optional[datetime] = None,
575
+ limit: int = 100
576
+ ) -> List[Dict[str, Any]]:
577
+ """Get log events from a specific log stream"""
578
+
579
+ async with self.aws.client('logs') as logs:
580
+ params = {
581
+ 'logGroupName': log_group_name,
582
+ 'logStreamName': log_stream_name,
583
+ 'limit': limit,
584
+ 'startFromHead': False # Get most recent events first
585
+ }
586
+
587
+ if start_time:
588
+ params['startTime'] = int(start_time.timestamp() * 1000)
589
+
590
+ response = await logs.get_log_events(**params)
591
+
592
+ return [
593
+ {
594
+ 'timestamp': datetime.fromtimestamp(
595
+ event['timestamp'] / 1000
596
+ ).isoformat(),
597
+ 'message': event['message']
598
+ }
599
+ for event in response.get('events', [])
600
+ ]
601
+
602
+ async def _put_metric_data(
603
+ self,
604
+ namespace: str,
605
+ metric_name: str,
606
+ metric_value: float,
607
+ dimensions: Optional[List[Dict[str, str]]] = None,
608
+ unit: Optional[str] = None
609
+ ) -> bool:
610
+ """Publish custom metric data"""
611
+
612
+ async with self.aws.client('cloudwatch') as cloudwatch:
613
+ metric_data = {
614
+ 'MetricName': metric_name,
615
+ 'Value': metric_value,
616
+ 'Timestamp': datetime.utcnow()
617
+ }
618
+
619
+ if dimensions:
620
+ metric_data['Dimensions'] = [
621
+ {'Name': d['Name'], 'Value': d['Value']}
622
+ for d in dimensions
623
+ ]
624
+
625
+ if unit:
626
+ metric_data['Unit'] = unit
627
+
628
+ await cloudwatch.put_metric_data(
629
+ Namespace=namespace,
630
+ MetricData=[metric_data]
631
+ )
632
+
633
+ return True
634
+
635
+ async def _describe_alarms(
636
+ self,
637
+ pattern: Optional[str] = None,
638
+ limit: int = 50
639
+ ) -> List[Dict[str, Any]]:
640
+ """List CloudWatch alarms"""
641
+
642
+ async with self.aws.client('cloudwatch') as cloudwatch:
643
+ params = {'MaxRecords': limit}
644
+ if pattern:
645
+ params['AlarmNamePrefix'] = pattern
646
+
647
+ response = await cloudwatch.describe_alarms(**params)
648
+
649
+ return [
650
+ {
651
+ 'name': alarm['AlarmName'],
652
+ 'description': alarm.get('AlarmDescription'),
653
+ 'state': alarm['StateValue'],
654
+ 'state_reason': alarm.get('StateReason'),
655
+ 'metric_name': alarm.get('MetricName'),
656
+ 'namespace': alarm.get('Namespace'),
657
+ 'comparison': alarm.get('ComparisonOperator'),
658
+ 'threshold': alarm.get('Threshold'),
659
+ 'evaluation_periods': alarm.get('EvaluationPeriods')
660
+ }
661
+ for alarm in response.get('MetricAlarms', [])
662
+ ]
663
+
664
+ async def _execute(self, **kwargs) -> ToolResult:
665
+ """Execute CloudWatch operation"""
666
+
667
+ try:
668
+ operation = kwargs['operation']
669
+
670
+ # Parse time parameters
671
+ start_time = self._parse_relative_time(
672
+ kwargs.get('start_time', '-1h')
673
+ )
674
+ end_time = self._parse_relative_time(
675
+ kwargs.get('end_time', 'now')
676
+ )
677
+
678
+ # Route to appropriate method
679
+ if operation == CloudWatchOperation.QUERY_LOGS:
680
+ log_group = kwargs.get('log_group_name') or self.default_log_group
681
+ if not log_group:
682
+ return ToolResult(
683
+ success=False,
684
+ status="error",
685
+ result=None,
686
+ error="log_group_name is required for query_logs operation",
687
+ metadata={},
688
+ timestamp=datetime.now(timezone.utc).isoformat()
689
+ )
690
+
691
+ query_string = kwargs.get('query_string')
692
+ if not query_string:
693
+ # Default query if none provided
694
+ query_string = (
695
+ "fields @timestamp, @message "
696
+ "| sort @timestamp desc "
697
+ f"| limit {kwargs.get('limit', 100)}"
698
+ )
699
+
700
+ results = await self._query_logs_insights(
701
+ log_group_name=log_group,
702
+ query_string=query_string,
703
+ start_time=start_time,
704
+ end_time=end_time,
705
+ limit=kwargs.get('limit', 100)
706
+ )
707
+
708
+ return ToolResult(
709
+ success=True,
710
+ status="completed",
711
+ result={
712
+ 'log_group': log_group,
713
+ 'query': query_string,
714
+ 'time_range': {
715
+ 'start': start_time.isoformat(),
716
+ 'end': end_time.isoformat()
717
+ },
718
+ 'results': results,
719
+ 'count': len(results)
720
+ },
721
+ error=None,
722
+ metadata={
723
+ 'operation': 'query_logs',
724
+ 'log_group': log_group
725
+ },
726
+ timestamp=datetime.now(timezone.utc).isoformat(),
727
+ )
728
+
729
+ elif operation == CloudWatchOperation.GET_METRICS:
730
+ if not kwargs.get('namespace') or not kwargs.get('metric_name'):
731
+ return ToolResult(
732
+ success=False,
733
+ status="error",
734
+ result=None,
735
+ error="namespace and metric_name are required for get_metrics",
736
+ metadata={},
737
+ timestamp=datetime.now(timezone.utc).isoformat()
738
+ )
739
+
740
+ metrics = await self._get_metrics(
741
+ namespace=kwargs['namespace'],
742
+ metric_name=kwargs['metric_name'],
743
+ dimensions=kwargs.get('dimensions', []),
744
+ statistic=kwargs.get('statistic', 'Average'),
745
+ start_time=start_time,
746
+ end_time=end_time,
747
+ period=kwargs.get('period', 60)
748
+ )
749
+
750
+ return ToolResult(
751
+ success=True,
752
+ status="completed",
753
+ result=metrics,
754
+ error=None,
755
+ metadata={
756
+ 'operation': 'get_metrics',
757
+ 'namespace': kwargs['namespace'],
758
+ 'metric_name': kwargs['metric_name']
759
+ },
760
+ timestamp=datetime.now(timezone.utc).isoformat()
761
+ )
762
+
763
+ elif operation == CloudWatchOperation.LIST_LOG_GROUPS:
764
+ log_groups = await self._list_log_groups(
765
+ pattern=kwargs.get('pattern'),
766
+ limit=kwargs.get('limit', 50)
767
+ )
768
+
769
+ return ToolResult(
770
+ success=True,
771
+ status="completed",
772
+ result={
773
+ 'log_groups': log_groups,
774
+ 'count': len(log_groups)
775
+ },
776
+ error=None,
777
+ metadata={
778
+ 'operation': 'list_log_groups',
779
+ 'pattern': kwargs.get('pattern')
780
+ },
781
+ timestamp=datetime.now(timezone.utc).isoformat()
782
+ )
783
+
784
+ elif operation == CloudWatchOperation.LIST_LOG_STREAMS:
785
+ if not kwargs.get('log_group_name'):
786
+ return ToolResult(
787
+ success=False,
788
+ status="error",
789
+ result=None,
790
+ error="log_group_name is required for list_log_streams",
791
+ metadata={},
792
+ timestamp=datetime.now(timezone.utc).isoformat()
793
+ )
794
+
795
+ log_streams = await self._list_log_streams(
796
+ log_group_name=kwargs['log_group_name'],
797
+ limit=kwargs.get('limit', 50)
798
+ )
799
+
800
+ return ToolResult(
801
+ success=True,
802
+ status="completed",
803
+ result={
804
+ 'log_group': kwargs['log_group_name'],
805
+ 'log_streams': log_streams,
806
+ 'count': len(log_streams)
807
+ },
808
+ error=None,
809
+ metadata={
810
+ 'operation': 'list_log_streams',
811
+ 'log_group': kwargs['log_group_name']
812
+ },
813
+ timestamp=datetime.now(timezone.utc).isoformat()
814
+ )
815
+
816
+ elif operation == CloudWatchOperation.GET_LOG_EVENTS:
817
+ if not kwargs.get('log_group_name') or not kwargs.get('log_stream_name'):
818
+ return ToolResult(
819
+ success=False,
820
+ status="error",
821
+ result=None,
822
+ error="log_group_name and log_stream_name are required",
823
+ metadata={},
824
+ timestamp=datetime.now(timezone.utc).isoformat()
825
+ )
826
+
827
+ events = await self._get_log_events(
828
+ log_group_name=kwargs['log_group_name'],
829
+ log_stream_name=kwargs['log_stream_name'],
830
+ start_time=start_time if kwargs.get('start_time') else None,
831
+ limit=kwargs.get('limit', 100)
832
+ )
833
+
834
+ return ToolResult(
835
+ success=True,
836
+ status="completed",
837
+ result={
838
+ 'log_group': kwargs['log_group_name'],
839
+ 'log_stream': kwargs['log_stream_name'],
840
+ 'events': events,
841
+ 'count': len(events)
842
+ },
843
+ error=None,
844
+ metadata={
845
+ 'operation': 'get_log_events',
846
+ 'log_group': kwargs['log_group_name'],
847
+ 'log_stream': kwargs['log_stream_name']
848
+ },
849
+ timestamp=datetime.now(timezone.utc).isoformat()
850
+ )
851
+
852
+ elif operation == CloudWatchOperation.LOG_SUMMARY:
853
+ if not kwargs.get('log_group_name'):
854
+ return ToolResult(
855
+ success=False,
856
+ status="error",
857
+ result=None,
858
+ error="log_group_name is required for log_summary",
859
+ metadata={},
860
+ timestamp=datetime.now(timezone.utc).isoformat()
861
+ )
862
+
863
+ summary = await self._log_summary(
864
+ log_group_name=kwargs['log_group_name'],
865
+ log_stream_name=kwargs.get('log_stream_name'),
866
+ start_time=start_time if kwargs.get('start_time') else None,
867
+ limit=kwargs.get('limit', 100),
868
+ max_message_length=kwargs.get('max_message_length', 500)
869
+ )
870
+
871
+ return ToolResult(
872
+ success=True,
873
+ status="completed",
874
+ result={
875
+ 'log_group': kwargs['log_group_name'],
876
+ 'log_stream': kwargs.get('log_stream_name'),
877
+ 'summary': summary,
878
+ 'count': len(summary),
879
+ 'max_message_length': kwargs.get('max_message_length', 500)
880
+ },
881
+ error=None,
882
+ metadata={
883
+ 'operation': 'log_summary',
884
+ 'log_group': kwargs['log_group_name'],
885
+ 'log_stream': kwargs.get('log_stream_name')
886
+ },
887
+ timestamp=datetime.now(timezone.utc).isoformat()
888
+ )
889
+
890
+ elif operation == CloudWatchOperation.PUT_METRIC_DATA:
891
+ if not all([
892
+ kwargs.get('namespace'),
893
+ kwargs.get('metric_name'),
894
+ kwargs.get('metric_value') is not None
895
+ ]):
896
+ return ToolResult(
897
+ success=False,
898
+ status="error",
899
+ result=None,
900
+ error="namespace, metric_name, and metric_value are required",
901
+ metadata={},
902
+ timestamp=datetime.now(timezone.utc).isoformat()
903
+ )
904
+
905
+ success = await self._put_metric_data(
906
+ namespace=kwargs['namespace'],
907
+ metric_name=kwargs['metric_name'],
908
+ metric_value=kwargs['metric_value'],
909
+ dimensions=kwargs.get('dimensions'),
910
+ unit=kwargs.get('unit')
911
+ )
912
+
913
+ return ToolResult(
914
+ success=True,
915
+ status="completed",
916
+ result={
917
+ 'message': 'Metric data published successfully',
918
+ 'namespace': kwargs['namespace'],
919
+ 'metric_name': kwargs['metric_name'],
920
+ 'value': kwargs['metric_value']
921
+ },
922
+ error=None,
923
+ metadata={
924
+ 'operation': 'put_metric_data',
925
+ 'namespace': kwargs['namespace'],
926
+ 'metric_name': kwargs['metric_name']
927
+ },
928
+ timestamp=datetime.now(timezone.utc).isoformat()
929
+ )
930
+
931
+ elif operation == CloudWatchOperation.DESCRIBE_ALARMS:
932
+ alarms = await self._describe_alarms(
933
+ pattern=kwargs.get('pattern'),
934
+ limit=kwargs.get('limit', 50)
935
+ )
936
+
937
+ return ToolResult(
938
+ success=True,
939
+ status="completed",
940
+ result={
941
+ 'alarms': alarms,
942
+ 'count': len(alarms)
943
+ },
944
+ error=None,
945
+ metadata={
946
+ 'operation': 'describe_alarms',
947
+ 'pattern': kwargs.get('pattern')
948
+ },
949
+ timestamp=datetime.now(timezone.utc).isoformat()
950
+ )
951
+
952
+ else:
953
+ return ToolResult(
954
+ success=False,
955
+ status="error",
956
+ result=None,
957
+ error=f"Unknown operation: {operation}",
958
+ metadata={'operation': str(operation)},
959
+ timestamp=datetime.now(timezone.utc).isoformat()
960
+ )
961
+
962
+ except ClientError as e:
963
+ error_code = e.response['Error']['Code']
964
+ error_msg = e.response['Error']['Message']
965
+ return ToolResult(
966
+ success=False,
967
+ status="aws_error",
968
+ result=None,
969
+ error=f"AWS Error ({error_code}): {error_msg}",
970
+ metadata={
971
+ 'error_code': error_code,
972
+ 'operation': kwargs.get('operation', 'unknown')
973
+ },
974
+ timestamp=datetime.now(timezone.utc).isoformat()
975
+ )
976
+
977
+ except Exception as e:
978
+ return ToolResult(
979
+ success=False,
980
+ status="error",
981
+ result=None,
982
+ error=f"CloudWatch operation failed: {str(e)}",
983
+ metadata={
984
+ 'operation': kwargs.get('operation', 'unknown'),
985
+ 'exception_type': type(e).__name__
986
+ },
987
+ timestamp=datetime.now(timezone.utc).isoformat()
988
+ )