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,337 @@
1
+ from typing import AsyncGenerator, Dict, Any
2
+ import asyncio
3
+ from aiohttp import web
4
+ from datamodel.parsers.json import json_encoder, json_decoder # pylint: disable=E0611 # noqa
5
+ from navigator.views import BaseHandler
6
+ from navigator_auth.conf import exclude_list
7
+ from parrot.bots import AbstractBot
8
+
9
+
10
+ class StreamHandler(BaseHandler):
11
+ """Streaming Endpoints for Parrot LLM Responses.
12
+
13
+ Supports:
14
+ - SSE (Server-Sent Events)
15
+ - WebSockets
16
+ """
17
+ def __init__(self, *args, **kwargs):
18
+ super().__init__(*args, **kwargs)
19
+ self.active_connections = set()
20
+
21
+ def _get_botmanager(self, request: web.Request):
22
+ """Retrieve the bot manager from the application context."""
23
+ try:
24
+ return request.app['bot_manager']
25
+ except KeyError as e:
26
+ raise web.HTTPInternalServerError(
27
+ reason="Bot manager not found in application."
28
+ ) from e
29
+
30
+ async def _get_bot(self, request: web.Request) -> AbstractBot:
31
+ """Retrieve the bot instance based on bot_id from the request."""
32
+ bot_manager = self._get_botmanager(request)
33
+ bot_id = request.match_info.get('bot_id')
34
+ bot = await bot_manager.get_bot(bot_id)
35
+ if bot is None:
36
+ raise web.HTTPNotFound(
37
+ reason=f"Bot with ID '{bot_id}' not found."
38
+ )
39
+ return bot
40
+
41
+ def _extract_stream_params(self, payload: Dict[str, Any], *extra_ignored_keys: str):
42
+ """Split incoming payload into prompt and kwargs for ask_stream."""
43
+ ignored_keys = {"prompt", *extra_ignored_keys}
44
+ prompt = payload.get('prompt', '')
45
+ kwargs = {k: v for k, v in payload.items() if k not in ignored_keys}
46
+ return prompt, kwargs
47
+
48
+ async def stream_sse(self, request: web.Request) -> web.StreamResponse:
49
+ """
50
+ Server-Sent Events (SSE) streaming endpoint
51
+ Best for: Unidirectional streaming, HTTP/1.1 compatible
52
+ """
53
+ data = await request.json()
54
+ prompt, ask_kwargs = self._extract_stream_params(data)
55
+ bot = await self._get_bot(request)
56
+ response = web.StreamResponse(
57
+ status=200,
58
+ reason='OK',
59
+ headers={
60
+ 'Content-Type': 'text/event-stream',
61
+ 'Cache-Control': 'no-cache',
62
+ 'Connection': 'keep-alive',
63
+ 'Access-Control-Allow-Origin': '*',
64
+ 'X-Accel-Buffering': 'no', # Disable nginx buffering
65
+ }
66
+ )
67
+ await response.prepare(request)
68
+ try:
69
+ async for chunk in bot.ask_stream(prompt, **ask_kwargs):
70
+ # Format as SSE
71
+ sse_data = f"data: {json_encoder({'content': chunk})}\n\n"
72
+ await response.write(sse_data.encode('utf-8'))
73
+ await response.drain()
74
+
75
+ # Send completion event
76
+ await response.write(b"data: [DONE]\n\n")
77
+ await response.drain()
78
+ except asyncio.CancelledError as e:
79
+ raise web.HTTPInternalServerError(
80
+ reason="Client disconnected during streaming."
81
+ ) from e
82
+ except Exception as e:
83
+ await response.write(
84
+ f"error: {str(e)}\n\n".encode('utf-8')
85
+ )
86
+ finally:
87
+ await response.write_eof()
88
+ return response
89
+
90
+ async def stream_ndjson(self, request: web.Request) -> web.StreamResponse:
91
+ """
92
+ NDJSON (Newline Delimited JSON) streaming endpoint
93
+ Best for: Clients that can parse JSON lines, more flexible than SSE
94
+ """
95
+ data = await request.json()
96
+ prompt, ask_kwargs = self._extract_stream_params(data)
97
+ bot = await self._get_bot(request)
98
+ response = web.StreamResponse(
99
+ status=200,
100
+ reason='OK',
101
+ headers={
102
+ 'Content-Type': 'application/x-ndjson',
103
+ 'Cache-Control': 'no-cache',
104
+ 'Connection': 'keep-alive',
105
+ 'Access-Control-Allow-Origin': '*',
106
+ }
107
+ )
108
+ await response.prepare(request)
109
+ try:
110
+ async for chunk in bot.ask_stream(prompt, **ask_kwargs):
111
+ # Each line is a complete JSON object
112
+ line = json_encoder({
113
+ 'type': 'content',
114
+ 'data': chunk,
115
+ 'timestamp': asyncio.get_event_loop().time()
116
+ }) + '\n'
117
+ await response.write(line.encode('utf-8'))
118
+ await response.drain()
119
+
120
+ # Send completion line
121
+ await response.write(
122
+ json_encoder({'done': True}).encode('utf-8') + b'\n'
123
+ )
124
+ await response.drain()
125
+ except asyncio.CancelledError as e:
126
+ raise web.HTTPInternalServerError(
127
+ reason="Client disconnected during streaming."
128
+ ) from e
129
+ except Exception as e:
130
+ error_line = json_encoder({'error': str(e)}) + '\n'
131
+ await response.write(error_line.encode('utf-8'))
132
+ finally:
133
+ await response.write_eof()
134
+ return response
135
+
136
+ async def stream_chunked(self, request: web.Request) -> web.StreamResponse:
137
+ """
138
+ Plain chunked transfer encoding
139
+ Best for: Simple text streaming without special formatting
140
+ """
141
+ data = await request.json()
142
+ prompt, ask_kwargs = self._extract_stream_params(data)
143
+ bot = await self._get_bot(request)
144
+ response = web.StreamResponse(
145
+ status=200,
146
+ reason='OK',
147
+ headers={
148
+ 'Content-Type': 'text/plain; charset=utf-8',
149
+ 'Transfer-Encoding': 'chunked',
150
+ 'Cache-Control': 'no-cache',
151
+ 'Connection': 'keep-alive',
152
+ 'Access-Control-Allow-Origin': '*',
153
+ }
154
+ )
155
+ await response.prepare(request)
156
+ try:
157
+ async for chunk in bot.ask_stream(prompt, **ask_kwargs):
158
+ await response.write(chunk.encode('utf-8'))
159
+ await response.drain()
160
+
161
+ # Indicate end of stream
162
+ await response.write_eof()
163
+ except asyncio.CancelledError as e:
164
+ raise web.HTTPInternalServerError(
165
+ reason="Client disconnected during streaming."
166
+ ) from e
167
+ except Exception as e:
168
+ await response.write(f"\n[ERROR]: {str(e)}\n".encode('utf-8'))
169
+ finally:
170
+ await response.write_eof()
171
+ return response
172
+
173
+ async def stream_websocket(self, request: web.Request) -> web.WebSocketResponse:
174
+ """
175
+ WebSocket endpoint for bidirectional streaming
176
+ Best for: Real-time bidirectional communication, chat applications
177
+ """
178
+ ws = web.WebSocketResponse(
179
+ heartbeat=30.0, # Send ping every 30s
180
+ max_msg_size=10 * 1024 * 1024 # 10MB max message size
181
+ )
182
+ # Extract and validate JWT from Sec-WebSocket-Protocol
183
+ # Client sends: new WebSocket(url, ["jwt", token])
184
+ # Header received: Sec-WebSocket-Protocol: jwt, <token>
185
+ protocol_header = request.headers.get('Sec-WebSocket-Protocol')
186
+ selected_protocol = None
187
+
188
+ if protocol_header:
189
+ parts = [p.strip() for p in protocol_header.split(',')]
190
+ if 'jwt' in parts:
191
+ # Find the token (assuming it's the other part)
192
+ # This is a bit naive if there are other protocols, but fits the requirement
193
+ try:
194
+ token_idx = parts.index('jwt') + 1
195
+ # If 'jwt' is the last item, try look elsewhere or it might be (token, jwt) order?
196
+ # Browsers usually send in order requested.
197
+ # Actually, if we send ["jwt", "token"], header is "jwt, token"
198
+ # But we need to identify WHICH one is the token.
199
+ # Heuristic: The token is the long string that isn't 'jwt'.
200
+ # Or simpler: remove 'jwt' and take the first remaining non-empty string.
201
+ parts.remove('jwt')
202
+ if parts:
203
+ token = parts[0]
204
+ if await self._validate_token(token):
205
+ # Auth success
206
+ # We MUST return the selected protocol in the response
207
+ # to the client, otherwise the WS connection fails.
208
+ # Usually we echo 'jwt' or the protocol used.
209
+ selected_protocol = 'jwt'
210
+ else:
211
+ raise web.HTTPUnauthorized(reason="Invalid Token")
212
+ else:
213
+ raise web.HTTPUnauthorized(reason="Missing Token")
214
+ except (ValueError, IndexError):
215
+ raise web.HTTPUnauthorized(reason="Invalid Protocol Format")
216
+
217
+ if selected_protocol:
218
+ await ws.prepare(request, protocols=[selected_protocol])
219
+ else:
220
+ await ws.prepare(request)
221
+
222
+ self.active_connections.add(ws)
223
+ bot = await self._get_bot(request)
224
+
225
+ try:
226
+ await ws.send_json({
227
+ 'type': 'connection',
228
+ 'status': 'connected',
229
+ 'message': 'WebSocket connection established'
230
+ })
231
+
232
+ async for msg in ws:
233
+ if msg.type == web.WSMsgType.TEXT:
234
+ # Handle incoming messages
235
+ try:
236
+ data = json_decoder(msg.data)
237
+ await self._handle_message(ws, data, bot)
238
+ except Exception:
239
+ await ws.send_json({
240
+ 'type': 'error',
241
+ 'message': 'Invalid JSON'
242
+ })
243
+ elif msg.type == web.WSMsgType.ERROR:
244
+ # Handle errors
245
+ print(f'WebSocket error: {ws.exception()}')
246
+ self.active_connections.remove(ws)
247
+ except Exception as e:
248
+ self.active_connections.remove(ws)
249
+ raise web.HTTPInternalServerError(
250
+ reason="Error occurred during WebSocket communication."
251
+ ) from e
252
+ return ws
253
+
254
+ async def _validate_token(self, token: str) -> bool:
255
+ """Validate the provided token using the app's auth system."""
256
+ if not token:
257
+ return False
258
+ # auth_manager = self.app.get('auth_manager')
259
+ # if not auth_manager:
260
+ # return False
261
+ # is_valid = await auth_manager.validate_token(token)
262
+ # return is_valid
263
+ return True # Temporarily allow all tokens for testing
264
+
265
+ async def _handle_message(self, ws: web.WebSocketResponse, data: dict, bot: AbstractBot):
266
+ """Handle incoming WebSocket messages"""
267
+ msg_type = data.get('type')
268
+ if msg_type == 'auth':
269
+ auth_header = data.get('authorization', '')
270
+ token = auth_header.replace('Bearer ', '') if auth_header.startswith('Bearer ') else None
271
+
272
+ if await self._validate_token(token):
273
+ ws._authenticated = True
274
+ await ws.send_json({'type': 'auth_success', 'message': 'Authentication successful'})
275
+ else:
276
+ await ws.send_json({'type': 'auth_error', 'message': 'Invalid or expired token'})
277
+ return
278
+
279
+ if msg_type == 'stream_request':
280
+ prompt, ask_kwargs = self._extract_stream_params(data, 'type')
281
+
282
+ # Send acknowledgment
283
+ await ws.send_json({
284
+ 'type': 'stream_start',
285
+ 'prompt': prompt
286
+ })
287
+
288
+ try:
289
+ # Stream the LLM response
290
+ async for chunk in bot.ask_stream(prompt, **ask_kwargs):
291
+ await ws.send_json({
292
+ 'type': 'content',
293
+ 'data': chunk
294
+ })
295
+
296
+ # Send completion
297
+ await ws.send_json({
298
+ 'type': 'stream_complete'
299
+ })
300
+
301
+ except Exception as e:
302
+ await ws.send_json({
303
+ 'type': 'error',
304
+ 'message': str(e)
305
+ })
306
+
307
+ elif msg_type == 'ping':
308
+ await ws.send_json({'type': 'pong'})
309
+
310
+ else:
311
+ await ws.send_json({
312
+ 'type': 'error',
313
+ 'message': f'Unknown message type: {msg_type}'
314
+ })
315
+
316
+ async def broadcast(self, message: dict):
317
+ """Broadcast message to all connected clients"""
318
+ for ws in self.active_connections:
319
+ try:
320
+ await ws.send_json(message)
321
+ except Exception as e:
322
+ print(f"Error broadcasting to client: {e}")
323
+
324
+ def configure_routes(self, app: web.Application):
325
+ """Configure routes for streaming endpoints."""
326
+ # sse endpoint
327
+ exclude_list.append('/bots/*/stream/sse')
328
+ app.router.add_post('/bots/{bot_id}/stream/sse', self.stream_sse)
329
+ # ndjson endpoint
330
+ exclude_list.append('/bots/*/stream/ndjson')
331
+ app.router.add_post('/bots/{bot_id}/stream/ndjson', self.stream_ndjson)
332
+ # chunked endpoint
333
+ exclude_list.append('/bots/*/stream/chunked')
334
+ app.router.add_post('/bots/{bot_id}/stream/chunked', self.stream_chunked)
335
+ # websocket endpoint
336
+ exclude_list.append('/bots/*/stream/ws')
337
+ app.router.add_get('/bots/{bot_id}/stream/ws', self.stream_websocket)
@@ -0,0 +1,6 @@
1
+ from .database import DBInterface
2
+
3
+
4
+ __all__ = (
5
+ 'DBInterface',
6
+ )
@@ -0,0 +1,143 @@
1
+ """
2
+ AWS Interface for AI-Parrot
3
+ Provides async context manager for AWS service clients using aioboto3
4
+ """
5
+ from typing import Optional, Dict, Any, AsyncIterator
6
+ from contextlib import asynccontextmanager
7
+ import aioboto3
8
+ from botocore.exceptions import ClientError, NoCredentialsError
9
+ from ..conf import (
10
+ AWS_ACCESS_KEY,
11
+ AWS_SECRET_KEY,
12
+ AWS_REGION_NAME,
13
+ AWS_CREDENTIALS
14
+ )
15
+
16
+
17
+ class AWSInterface:
18
+ """
19
+ Base interface for AWS services using aioboto3.
20
+
21
+ Provides async context manager for creating service clients.
22
+ Handles credential management and session lifecycle.
23
+
24
+ Example:
25
+ >>> aws = AWSInterface(aws_id='default')
26
+ >>> async with aws.client('s3') as s3:
27
+ ... response = await s3.list_buckets()
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ aws_id: str = 'default',
33
+ region_name: Optional[str] = None,
34
+ credentials: Optional[Dict[str, Any]] = None,
35
+ **kwargs
36
+ ):
37
+ """
38
+ Initialize AWS interface.
39
+
40
+ Args:
41
+ aws_id: Identifier for credentials in AWS_CREDENTIALS dict
42
+ region_name: AWS region (overrides credentials config)
43
+ credentials: Direct credential dict (overrides aws_id lookup)
44
+ **kwargs: Additional boto3 session parameters
45
+ """
46
+ # Get credentials from config or direct input
47
+ if credentials is None:
48
+ credentials = AWS_CREDENTIALS.get(aws_id, {})
49
+ if not credentials or credentials == 'default':
50
+ credentials = AWS_CREDENTIALS.get('default', {
51
+ 'aws_key': AWS_ACCESS_KEY,
52
+ 'aws_secret': AWS_SECRET_KEY,
53
+ 'region_name': AWS_REGION_NAME
54
+ })
55
+
56
+ # Build AWS config
57
+ self.aws_config = {
58
+ 'aws_access_key_id': credentials.get('aws_key') or credentials.get('aws_access_key_id'),
59
+ 'aws_secret_access_key': credentials.get('aws_secret') or credentials.get('aws_secret_access_key'),
60
+ 'region_name': region_name or credentials.get('region_name', AWS_REGION_NAME),
61
+ }
62
+
63
+ # Add optional session token if present
64
+ if 'aws_session_token' in credentials:
65
+ self.aws_config['aws_session_token'] = credentials['aws_session_token']
66
+
67
+ # Add any additional kwargs
68
+ self.aws_config.update(kwargs)
69
+
70
+ # Remove None values
71
+ self.aws_config = {k: v for k, v in self.aws_config.items() if v is not None}
72
+
73
+ # Create session
74
+ self.session = aioboto3.Session(**self.aws_config)
75
+ self._region = self.aws_config.get('region_name')
76
+
77
+ @property
78
+ def region(self) -> str:
79
+ """Get configured AWS region"""
80
+ return self._region
81
+
82
+ @asynccontextmanager
83
+ async def client(self, service_name: str, **kwargs) -> AsyncIterator[Any]:
84
+ """
85
+ Async context manager for AWS service client.
86
+
87
+ Args:
88
+ service_name: AWS service name (e.g., 's3', 'cloudwatch', 'logs')
89
+ **kwargs: Additional client configuration
90
+
91
+ Yields:
92
+ AWS service client
93
+
94
+ Example:
95
+ >>> async with aws.client('cloudwatch') as cw:
96
+ ... metrics = await cw.list_metrics()
97
+ """
98
+ async with self.session.client(service_name, **kwargs) as client:
99
+ yield client
100
+
101
+ @asynccontextmanager
102
+ async def resource(self, service_name: str, **kwargs) -> AsyncIterator[Any]:
103
+ """
104
+ Async context manager for AWS service resource.
105
+
106
+ Args:
107
+ service_name: AWS service name (e.g., 's3', 'dynamodb')
108
+ **kwargs: Additional resource configuration
109
+
110
+ Yields:
111
+ AWS service resource
112
+ """
113
+ async with self.session.resource(service_name, **kwargs) as resource:
114
+ yield resource
115
+
116
+ async def validate_credentials(self) -> bool:
117
+ """
118
+ Validate AWS credentials by making a simple API call.
119
+
120
+ Returns:
121
+ True if credentials are valid, False otherwise
122
+ """
123
+ try:
124
+ async with self.client('sts') as sts:
125
+ await sts.get_caller_identity()
126
+ return True
127
+ except (ClientError, NoCredentialsError):
128
+ return False
129
+
130
+ async def get_caller_identity(self) -> Dict[str, Any]:
131
+ """
132
+ Get AWS caller identity information.
133
+
134
+ Returns:
135
+ Dict with UserId, Account, and Arn
136
+ """
137
+ async with self.client('sts') as sts:
138
+ response = await sts.get_caller_identity()
139
+ return {
140
+ 'user_id': response.get('UserId'),
141
+ 'account': response.get('Account'),
142
+ 'arn': response.get('Arn')
143
+ }
@@ -0,0 +1,113 @@
1
+ import os
2
+ from abc import ABC
3
+ import contextlib
4
+ from typing import TypeVar
5
+ from typing_extensions import ParamSpec
6
+ from navconfig import config
7
+ from navconfig.logging import logging
8
+
9
+
10
+ P = ParamSpec("P")
11
+ T = TypeVar("T")
12
+
13
+
14
+ valid_types = {
15
+ "<class 'str'>": str,
16
+ "<class 'int'>": int,
17
+ "<class 'float'>": float,
18
+ "<class 'list'>": list,
19
+ "<class 'tuple'>": tuple,
20
+ "<class 'dict'>": dict
21
+ }
22
+
23
+
24
+ class CredentialsInterface(ABC):
25
+ """
26
+ Abstract Base Class for handling credentials and environment variables.
27
+ This class provides methods to process and validate credentials, as well as
28
+ retrieve values from environment variables or configuration files.
29
+ """
30
+ _credentials: dict = {"username": str, "password": str}
31
+
32
+ def __init__(self, *args, **kwargs) -> None:
33
+ # if credentials:
34
+ self.credentials: dict = kwargs.pop('credentials', None)
35
+ self._no_warnings = kwargs.get("no_warnings", False)
36
+ if expected := kwargs.pop("expected_credentials", None):
37
+ self._credentials = expected
38
+ self._environment = config
39
+ try:
40
+ super().__init__(*args, **kwargs)
41
+ except TypeError:
42
+ super().__init__()
43
+ # Interface not started:
44
+ self._started: bool = False
45
+ self.logger = logging.getLogger(__name__)
46
+
47
+ def get_env_value(self, key, default: str = None, expected_type: object = None):
48
+ """
49
+ Retrieves a value from the environment variables or the configuration.
50
+
51
+ :param key: The key for the environment variable.
52
+ :param default: The default value to return if the key is not found.
53
+ :return: The value of the environment variable or the default value.
54
+ """
55
+ if key is None:
56
+ return default
57
+ if expected_type is not None:
58
+ with contextlib.suppress(TypeError):
59
+ if expected_type in (int, float):
60
+ return val if (val := self._environment.getint(key)) else key
61
+ elif expected_type == bool:
62
+ return val if (val := self._environment.getboolean(key)) else key
63
+ else:
64
+ return val if (val := self._environment.get(key)) else key
65
+ return default
66
+ if val := os.getenv(str(key), default):
67
+ return val
68
+ return val if (val := self._environment.get(key, default)) else key
69
+
70
+ def processing_credentials(self):
71
+ if self.credentials:
72
+ for key, expected_type in self._credentials.items():
73
+ try:
74
+ value = self.credentials.get(key, None)
75
+ default = getattr(self, key, value)
76
+ # print('KEY ', key, 'VAL ', value, 'DEF ', default)
77
+ if type(value) == expected_type or isinstance(value, valid_types[str(expected_type)]): # pylint: disable=E1136 # noqa
78
+ # can process the credentials, extracted from environment or variables:
79
+ val = self.get_env_value(
80
+ value, default=default, expected_type=expected_type
81
+ )
82
+ # print('VAL > ', val, 'DEFAULT > ', default, expected_type)
83
+ self.credentials[key] = val
84
+ # print('KEY: ', key, self.credentials[key])
85
+ elif isinstance(value, str):
86
+ # Use os.getenv to get the value from environment variables
87
+ env_value = self.get_env_value(
88
+ value, default=default, expected_type=expected_type
89
+ )
90
+ self.credentials[key] = env_value
91
+ else:
92
+ self.credentials[key] = default
93
+ except KeyError as exc:
94
+ print(f'Failed credential {key} with value {value}: {exc}')
95
+ continue
96
+ except (TypeError, ValueError) as ex:
97
+ self.logger.error(f"{__name__}: Wrong or missing Credentials")
98
+ raise RuntimeError(
99
+ f"{__name__}: Wrong or missing Credentials"
100
+ ) from ex
101
+ except Exception as ex:
102
+ self.logger.exception(
103
+ f"Error Processing Credentials: {ex}"
104
+ )
105
+ raise RuntimeError(
106
+ f"Error Processing Credentials: {ex}"
107
+ ) from ex
108
+ if self.credentials is None:
109
+ if self._no_warnings is False:
110
+ self.logger.warning(
111
+ "No credentials where Found."
112
+ )
113
+ self.credentials = {}
@@ -0,0 +1,27 @@
1
+ """DB (asyncdb) Extension.
2
+ DB connection for any Application.
3
+ """
4
+ from asyncdb import AsyncDB
5
+
6
+
7
+ class DBInterface:
8
+ """
9
+ Interface for using database connections in an Application using AsyncDB.
10
+ """
11
+
12
+ def get_database(
13
+ self,
14
+ driver: str,
15
+ dsn: str = None,
16
+ params: dict = None,
17
+ timeout: int = 60,
18
+ **kwargs
19
+ ) -> AsyncDB:
20
+ """Get the driver."""
21
+ return AsyncDB(
22
+ driver,
23
+ dsn=dsn,
24
+ params=params,
25
+ timeout=timeout,
26
+ **kwargs
27
+ )