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,651 @@
1
+ from typing import Optional, Union, Dict, Any, Tuple
2
+ import random
3
+ import os
4
+ import asyncio
5
+ import ssl
6
+ from pathlib import Path
7
+ from concurrent.futures import ThreadPoolExecutor
8
+ from io import BytesIO
9
+ from email.message import Message
10
+ from urllib import parse
11
+ from urllib.parse import urlencode, urlparse
12
+ import urllib3
13
+ import aiofiles
14
+ # parsing:
15
+ from bs4 import BeautifulSoup as bs
16
+ from lxml import html, etree
17
+ # backoff retries:
18
+ import backoff
19
+ # aiohttp:
20
+ import aiohttp
21
+ from aiohttp import BasicAuth
22
+ # httpx
23
+ import httpx
24
+ # config:
25
+ from datamodel.typedefs import SafeDict
26
+ from datamodel.parsers.json import JSONContent, json_encoder # pylint: disable=E0611 # noqa
27
+ from navconfig.logging import logging
28
+ from proxylists.proxies import FreeProxy, Oxylabs
29
+ from ..conf import (
30
+ HTTPCLIENT_MAX_SEMAPHORE,
31
+ HTTPCLIENT_MAX_WORKERS,
32
+ GOOGLE_SEARCH_API_KEY,
33
+ GOOGLE_SEARCH_ENGINE_ID
34
+ )
35
+
36
+
37
+ # Suppress warnings
38
+ logging.getLogger("urllib3").setLevel(logging.WARNING)
39
+ urllib3.disable_warnings()
40
+ for logger_name in ["httpx", "httpcore", "hpack"]:
41
+ logging.getLogger(logger_name).setLevel(logging.WARNING)
42
+
43
+
44
+ # User agents and impersonation profiles
45
+ UA_LIST = [
46
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
47
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
48
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
49
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0",
50
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
51
+ ]
52
+
53
+ MOBILE_UA_LIST = [
54
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
55
+ "Mozilla/5.0 (Linux; Android 13; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Mobile Safari/537.36",
56
+ ]
57
+
58
+ IMPERSONATES = (
59
+ "chrome_120", "chrome_123", "chrome_124", "chrome_126", "chrome_127", "chrome_128",
60
+ "chrome_129", "chrome_130", "chrome_131",
61
+ "safari_ios_18.1.1", "safari_18", "safari_18.2", "safari_ipad_18",
62
+ "edge_127", "edge_131", "firefox_128", "firefox_133",
63
+ )
64
+
65
+ VALID_METHODS = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']
66
+
67
+
68
+ class BackoffConfig:
69
+ """Configuration for backoff retry behavior."""
70
+
71
+ def __init__(
72
+ self,
73
+ max_tries: int = 3,
74
+ max_time: int = 120,
75
+ exponential: bool = True,
76
+ jitter: bool = True,
77
+ retry_statuses: tuple = (429, 500, 502, 503, 504)
78
+ ):
79
+ self.max_tries = max_tries
80
+ self.max_time = max_time
81
+ self.wait_gen = backoff.expo if exponential else backoff.constant
82
+ self.jitter = backoff.full_jitter if jitter else None
83
+ self.retry_statuses = retry_statuses
84
+
85
+
86
+ class HTTPService:
87
+ """
88
+ Unified HTTP client abstraction supporting both aiohttp and httpx.
89
+
90
+ Features:
91
+ - Proxy support (free and paid)
92
+ - User-Agent rotation
93
+ - Automatic retry with backoff
94
+ - HTTP/2 support
95
+ - Session management
96
+ - Response processing or raw response
97
+ """
98
+
99
+ def __init__(self, *args, **kwargs):
100
+ # Proxy configuration
101
+ self.use_proxy: bool = kwargs.pop("use_proxy", False)
102
+ self._free_proxy: bool = kwargs.pop('free_proxy', False)
103
+ self._proxies: list = []
104
+
105
+ # User-Agent configuration
106
+ self.rotate_ua: bool = kwargs.pop("rotate_ua", True)
107
+ self._ua: str = random.choice(UA_LIST) if self.rotate_ua else UA_LIST[0]
108
+
109
+ # HTTP configuration
110
+ self.use_async: bool = kwargs.pop("use_async", True)
111
+ self.timeout: int = kwargs.get('timeout', 30)
112
+ self.use_streams: bool = kwargs.get('use_streams', True)
113
+ self.as_binary: bool = kwargs.get('as_binary', False)
114
+ self.accept: str = kwargs.get('accept', "application/json")
115
+
116
+ # HTTP/2 support
117
+ self.use_http2: bool = kwargs.pop('use_http2', False)
118
+
119
+ # Backoff configuration
120
+ backoff_config = kwargs.pop('backoff_config', None)
121
+ if backoff_config and isinstance(backoff_config, dict):
122
+ self.backoff_config = BackoffConfig(**backoff_config)
123
+ elif isinstance(backoff_config, BackoffConfig):
124
+ self.backoff_config = backoff_config
125
+ else:
126
+ self.backoff_config = BackoffConfig()
127
+
128
+ # Headers setup
129
+ self.headers: dict = kwargs.get('headers', {})
130
+ self.headers.update({
131
+ "Accept": self.accept,
132
+ "Accept-Encoding": "gzip, deflate, br" if self.use_http2 else "gzip, deflate",
133
+ "DNT": "1",
134
+ "Connection": "keep-alive",
135
+ "Upgrade-Insecure-Requests": "1",
136
+ "User-Agent": self._ua,
137
+ })
138
+
139
+ # Cookies
140
+ self.cookies = kwargs.get('cookies', {})
141
+
142
+ # Authentication
143
+ self.credentials: dict = kwargs.get('credentials', {})
144
+ self.auth_type: str = None
145
+ self.token_type: str = "Bearer"
146
+ self._user, self._pwd = None, None
147
+ self._setup_auth()
148
+
149
+ # Google API
150
+ self.google_api_key: str = kwargs.pop('google_api_key', GOOGLE_SEARCH_API_KEY)
151
+ self.google_cse: str = kwargs.pop('google_cse', GOOGLE_SEARCH_ENGINE_ID)
152
+
153
+ # Response handling
154
+ self.no_errors: dict = kwargs.get('no_errors', {})
155
+ self._default_parser: str = kwargs.pop('bs4_parser', 'html.parser')
156
+
157
+ # Utilities
158
+ self._encoder = JSONContent()
159
+ self._executor = ThreadPoolExecutor(max_workers=int(HTTPCLIENT_MAX_WORKERS))
160
+ self._semaphore = asyncio.Semaphore(int(HTTPCLIENT_MAX_SEMAPHORE))
161
+ self._debug: bool = kwargs.pop('debug', False)
162
+ self.logger = logging.getLogger('Parrot.HTTPService')
163
+
164
+ # Store remaining arguments
165
+ self.arguments = kwargs
166
+
167
+ def _setup_auth(self):
168
+ """Setup authentication based on credentials."""
169
+ if "apikey" in self.credentials:
170
+ self.auth_type = "api_key"
171
+ self.headers["Authorization"] = f"{self.token_type} {self.credentials['apikey']}"
172
+ elif "username" in self.credentials:
173
+ self.auth_type = "basic"
174
+ self._user = self.credentials["username"]
175
+ self._pwd = self.credentials["password"]
176
+ elif "token" in self.credentials:
177
+ self.auth_type = "token"
178
+ self.headers["Authorization"] = f"{self.token_type} {self.credentials['token']}"
179
+ elif "key" in self.credentials:
180
+ self.auth_type = "key"
181
+
182
+ async def get_proxies(self, session_time: float = 1) -> list:
183
+ """Get proxy list (free or paid)."""
184
+ if self._free_proxy:
185
+ return await FreeProxy().get_list()
186
+ else:
187
+ return await Oxylabs(session_time=session_time, timeout=10).get_list()
188
+
189
+ async def refresh_proxies(self):
190
+ """Refresh the proxy list."""
191
+ if self.use_proxy:
192
+ self._proxies = await self.get_proxies()
193
+
194
+ def build_url(
195
+ self,
196
+ url: str,
197
+ queryparams: Optional[str] = None,
198
+ args: Optional[dict] = None,
199
+ params: Optional[dict] = None
200
+ ) -> str:
201
+ """Build complete URL with query parameters."""
202
+ if args:
203
+ url = str(url).format_map(SafeDict(**args))
204
+ if queryparams:
205
+ url += ("&" if "?" in url else "?") + queryparams
206
+ if params:
207
+ url += ("&" if "?" in url else "?") + urlencode(params)
208
+
209
+ if self._debug:
210
+ self.logger.debug(f"Built URL: {url}")
211
+ return url
212
+
213
+ def _create_ssl_context(self, verify_ssl: bool = True) -> ssl.SSLContext:
214
+ """Create SSL context with secure defaults."""
215
+ ssl_context = ssl.create_default_context()
216
+ ssl_context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
217
+ ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
218
+
219
+ if verify_ssl:
220
+ ssl_context.check_hostname = True
221
+ ssl_context.verify_mode = ssl.CERT_REQUIRED
222
+ else:
223
+ ssl_context.check_hostname = False
224
+ ssl_context.verify_mode = ssl.CERT_NONE
225
+
226
+ return ssl_context
227
+
228
+ def _should_retry(self, exception) -> bool:
229
+ """Determine if request should be retried based on exception."""
230
+ if isinstance(exception, (httpx.HTTPStatusError, aiohttp.ClientResponseError)):
231
+ status = getattr(exception, 'status_code', getattr(exception, 'status', None))
232
+ return status in self.backoff_config.retry_statuses
233
+ return isinstance(exception, (
234
+ httpx.TimeoutException,
235
+ aiohttp.ServerTimeoutError,
236
+ aiohttp.ClientError
237
+ ))
238
+
239
+ @backoff.on_exception(
240
+ backoff.expo,
241
+ (httpx.HTTPStatusError, httpx.TimeoutException, httpx.RequestError),
242
+ max_tries=3,
243
+ max_time=120,
244
+ jitter=backoff.full_jitter,
245
+ giveup=lambda e: isinstance(e, httpx.HTTPStatusError) and
246
+ e.response.status_code not in [429, 500, 502, 503, 504]
247
+ )
248
+ async def httpx_request(
249
+ self,
250
+ url: str,
251
+ method: str = 'GET',
252
+ headers: Optional[Dict[str, str]] = None,
253
+ cookies: Optional[httpx.Cookies] = None,
254
+ params: Optional[Dict[str, Any]] = None,
255
+ data: Optional[Dict[str, Any]] = None,
256
+ use_json: bool = False,
257
+ use_proxy: bool = None,
258
+ use_ssl: bool = True,
259
+ verify_ssl: bool = True,
260
+ follow_redirects: bool = True,
261
+ full_response: bool = False,
262
+ download: bool = False,
263
+ filename: Optional[str] = None,
264
+ timeout: Optional[Union[int, float]] = None,
265
+ **kwargs
266
+ ) -> Tuple[Any, Optional[Dict[str, Any]]]:
267
+ """
268
+ Make async HTTP request using httpx with HTTP/2 support.
269
+
270
+ Returns:
271
+ Tuple[result, error]: Processed result or raw response, and any error
272
+ """
273
+ # Determine proxy usage
274
+ use_proxy = use_proxy if use_proxy is not None else self.use_proxy
275
+ proxy_config = None
276
+
277
+ if use_proxy:
278
+ proxies = await self.get_proxies()
279
+ if proxies:
280
+ proxy = proxies[0]
281
+ proxy_config = {
282
+ "http://": f"http://{proxy}",
283
+ "https://": f"http://{proxy}"
284
+ }
285
+
286
+ # SSL configuration
287
+ ssl_context = self._create_ssl_context(verify_ssl) if use_ssl else None
288
+
289
+ # Setup transport with retry capability
290
+ transport = httpx.AsyncHTTPTransport(
291
+ retries=kwargs.pop('num_retries', 2),
292
+ verify=ssl_context
293
+ )
294
+
295
+ # Timeout configuration
296
+ timeout_config = httpx.Timeout(
297
+ timeout=timeout or self.timeout,
298
+ connect=kwargs.pop('connect_timeout', 5.0),
299
+ read=kwargs.pop('read_timeout', 20.0),
300
+ write=kwargs.pop('write_timeout', 5.0),
301
+ pool=kwargs.pop('pool_timeout', 20.0)
302
+ )
303
+
304
+ # Merge headers
305
+ request_headers = {**self.headers, **(headers or {})}
306
+
307
+ method = method.upper()
308
+ if method not in VALID_METHODS:
309
+ raise ValueError(f"Invalid HTTP method: {method}")
310
+
311
+ async with httpx.AsyncClient(
312
+ transport=transport,
313
+ headers=request_headers,
314
+ cookies=cookies,
315
+ proxy=proxy_config or None,
316
+ timeout=timeout_config,
317
+ http2=self.use_http2, # Enable HTTP/2
318
+ follow_redirects=follow_redirects,
319
+ **kwargs
320
+ ) as client:
321
+ try:
322
+ # Build request arguments
323
+ request_args = {
324
+ "method": method,
325
+ "url": url,
326
+ }
327
+
328
+ if data:
329
+ request_args["json" if use_json else "data"] = data
330
+ if params:
331
+ request_args["params"] = params
332
+
333
+ # Make request
334
+ response = await client.request(**request_args)
335
+ response.raise_for_status()
336
+
337
+ # Return full response if requested
338
+ if full_response:
339
+ return response, None
340
+
341
+ # Process response
342
+ result, error = await self._process_response(
343
+ response,
344
+ url,
345
+ download=download,
346
+ filename=filename
347
+ )
348
+ return result, error
349
+
350
+ except httpx.TimeoutException as e:
351
+ self.logger.error(f"Request timeout: {url}")
352
+ raise
353
+ except httpx.HTTPStatusError as e:
354
+ self.logger.error(f"HTTP {e.response.status_code}: {url}")
355
+ raise
356
+ except httpx.RequestError as e:
357
+ self.logger.error(f"Request error: {e}")
358
+ raise
359
+
360
+ @backoff.on_exception(
361
+ backoff.expo,
362
+ (aiohttp.ClientError, aiohttp.ServerTimeoutError, aiohttp.ClientResponseError),
363
+ max_tries=3,
364
+ max_time=60,
365
+ jitter=backoff.full_jitter,
366
+ giveup=lambda e: isinstance(e, aiohttp.ClientResponseError) and
367
+ e.status not in [429, 500, 502, 503, 504]
368
+ )
369
+ async def aiohttp_request(
370
+ self,
371
+ url: str,
372
+ method: str = 'GET',
373
+ headers: Optional[Dict[str, str]] = None,
374
+ data: Optional[Dict[str, Any]] = None,
375
+ use_json: bool = False,
376
+ use_proxy: bool = None,
377
+ use_ssl: bool = False,
378
+ verify_ssl: bool = True,
379
+ full_response: bool = False,
380
+ download: bool = False,
381
+ filename: Optional[str] = None,
382
+ timeout: Optional[Union[int, float]] = None,
383
+ **kwargs
384
+ ) -> Tuple[Any, Optional[Dict[str, Any]]]:
385
+ """
386
+ Make async HTTP request using aiohttp.
387
+
388
+ Returns:
389
+ Tuple[result, error]: Processed result or raw response, and any error
390
+ """
391
+ # Determine proxy usage
392
+ use_proxy = use_proxy if use_proxy is not None else self.use_proxy
393
+ proxy = None
394
+
395
+ if use_proxy:
396
+ proxies = await self.get_proxies()
397
+ if proxies:
398
+ proxy = random.choice(proxies)
399
+
400
+ # Authentication setup
401
+ auth = None
402
+ if self.auth_type == "basic" and self._user:
403
+ auth = BasicAuth(self._user, self._pwd)
404
+
405
+ # SSL configuration
406
+ ssl_context = self._create_ssl_context(verify_ssl) if use_ssl else None
407
+
408
+ # Merge headers
409
+ request_headers = {**self.headers, **(headers or {})}
410
+
411
+ # Timeout configuration
412
+ timeout_config = aiohttp.ClientTimeout(total=timeout or self.timeout)
413
+
414
+ method = method.upper()
415
+
416
+ async with aiohttp.ClientSession(
417
+ headers=request_headers,
418
+ timeout=timeout_config,
419
+ auth=auth,
420
+ json_serialize=json_encoder,
421
+ ) as session:
422
+ try:
423
+ # Build request kwargs
424
+ request_kwargs = {
425
+ "proxy": proxy,
426
+ "ssl": ssl_context if ssl_context else use_ssl,
427
+ }
428
+
429
+ # Make request
430
+ if use_json and data:
431
+ async with session.request(method, url, json=data, **request_kwargs) as response:
432
+ return await self._handle_aiohttp_response(
433
+ response, url, full_response, download, filename
434
+ )
435
+ else:
436
+ async with session.request(method, url, data=data, **request_kwargs) as response:
437
+ return await self._handle_aiohttp_response(
438
+ response, url, full_response, download, filename
439
+ )
440
+
441
+ except aiohttp.ClientError as e:
442
+ self.logger.error(f"aiohttp error: {e}")
443
+ raise
444
+
445
+ async def _handle_aiohttp_response(
446
+ self,
447
+ response,
448
+ url: str,
449
+ full_response: bool,
450
+ download: bool,
451
+ filename: Optional[str]
452
+ ) -> Tuple[Any, Optional[Dict[str, Any]]]:
453
+ """Handle aiohttp response."""
454
+ if full_response:
455
+ return response, None
456
+
457
+ # Check status
458
+ if response.status >= 400:
459
+ raise aiohttp.ClientResponseError(
460
+ request_info=response.request_info,
461
+ history=response.history,
462
+ status=response.status,
463
+ message=f"HTTP {response.status}",
464
+ headers=response.headers
465
+ )
466
+
467
+ return await self._process_response(response, url, download, filename)
468
+
469
+ async def _process_response(
470
+ self,
471
+ response,
472
+ url: str,
473
+ download: bool = False,
474
+ filename: Optional[str] = None
475
+ ) -> Tuple[Any, Optional[Any]]:
476
+ """
477
+ Unified response processing for both httpx and aiohttp.
478
+
479
+ Returns:
480
+ Tuple[result, error]
481
+ """
482
+ error = None
483
+ result = None
484
+
485
+ # Download handling
486
+ if download:
487
+ result = await self._handle_download(response, url, filename)
488
+ return result, error
489
+
490
+ # Content type based processing
491
+ if self.accept == 'application/octet-stream':
492
+ data = await self._get_response_content(response)
493
+ buffer = BytesIO(data)
494
+ buffer.seek(0)
495
+ result = buffer
496
+
497
+ elif self.accept in ('text/html', 'application/xhtml+xml', 'application/xml'):
498
+ content = await self._get_response_content(response)
499
+ try:
500
+ if self.accept == 'text/html':
501
+ result = bs(content, self._default_parser)
502
+ else:
503
+ result = etree.fromstring(content)
504
+ except Exception as e:
505
+ error = e
506
+ result = content
507
+
508
+ elif self.accept == "application/json":
509
+ try:
510
+ result = await self._get_response_json(response)
511
+ except Exception as e:
512
+ # Fallback to text/HTML parsing
513
+ try:
514
+ text = await self._get_response_text(response)
515
+ result = bs(text, self._default_parser)
516
+ except Exception:
517
+ error = e
518
+
519
+ elif self.as_binary:
520
+ result = await self._get_response_content(response)
521
+ else:
522
+ result = await self._get_response_text(response)
523
+
524
+ return result, error
525
+
526
+ async def _handle_download(
527
+ self,
528
+ response,
529
+ url: str,
530
+ filename: Optional[str] = None
531
+ ) -> Path:
532
+ """Handle file download."""
533
+ if not filename:
534
+ filename = os.path.basename(url)
535
+
536
+ # Try to get filename from headers
537
+ content_disposition = response.headers.get("content-disposition")
538
+ if content_disposition:
539
+ msg = Message()
540
+ msg["Content-Disposition"] = content_disposition
541
+ header_filename = msg.get_param("filename", header="Content-Disposition")
542
+ utf8_filename = msg.get_param("filename*", header="Content-Disposition")
543
+
544
+ if utf8_filename:
545
+ _, utf8_filename = utf8_filename.split("''", 1)
546
+ filename = parse.unquote(utf8_filename)
547
+ elif header_filename:
548
+ filename = header_filename
549
+
550
+ filepath = Path(filename)
551
+ filepath.parent.mkdir(parents=True, exist_ok=True)
552
+
553
+ if filepath.exists():
554
+ self.logger.warning(f"File already exists: {filepath}")
555
+ return filepath
556
+
557
+ # Download file
558
+ total_size = response.headers.get("Content-Length")
559
+ self.logger.info(f"Downloading: {filepath} (size: {total_size})")
560
+
561
+ # Stream download
562
+ if self.use_streams and hasattr(response, 'content'):
563
+ async with aiofiles.open(filepath, 'wb') as f:
564
+ if hasattr(response.content, 'iter_chunked'): # aiohttp
565
+ async for chunk in response.content.iter_chunked(8192):
566
+ await f.write(chunk)
567
+ else: # httpx
568
+ async for chunk in response.aiter_bytes(8192):
569
+ await f.write(chunk)
570
+ else:
571
+ content = await self._get_response_content(response)
572
+ async with aiofiles.open(filepath, 'wb') as f:
573
+ await f.write(content)
574
+
575
+ self.logger.info(f"Downloaded: {filepath}")
576
+ return filepath
577
+
578
+ async def _get_response_json(self, response):
579
+ """Get JSON from response (handles both httpx and aiohttp)."""
580
+ if hasattr(response, 'json'):
581
+ if asyncio.iscoroutinefunction(response.json):
582
+ return await response.json()
583
+ return response.json()
584
+ raise ValueError("Response does not support JSON")
585
+
586
+ async def _get_response_text(self, response):
587
+ """Get text from response (handles both httpx and aiohttp)."""
588
+ if hasattr(response, 'text'):
589
+ if asyncio.iscoroutinefunction(response.text):
590
+ return await response.text()
591
+ return response.text
592
+ raise ValueError("Response does not support text")
593
+
594
+ async def _get_response_content(self, response):
595
+ """Get binary content from response (handles both httpx and aiohttp)."""
596
+ if hasattr(response, 'content'):
597
+ if asyncio.iscoroutinefunction(response.content):
598
+ return await response.content()
599
+ return response.content
600
+ if hasattr(response, 'read'):
601
+ return await response.read()
602
+ if hasattr(response, 'aread'):
603
+ return await response.aread()
604
+ raise ValueError("Response does not support content reading")
605
+
606
+ async def request(
607
+ self,
608
+ url: str,
609
+ method: str = 'GET',
610
+ client: str = 'httpx',
611
+ **kwargs
612
+ ) -> Tuple[Any, Optional[Dict[str, Any]]]:
613
+ """
614
+ Unified request method that delegates to httpx or aiohttp.
615
+
616
+ Args:
617
+ url: URL to request
618
+ method: HTTP method
619
+ client: 'httpx' or 'aiohttp' (default: 'httpx')
620
+ **kwargs: Additional arguments passed to specific client
621
+
622
+ Returns:
623
+ Tuple[result, error]
624
+ """
625
+ if client.lower() == 'httpx':
626
+ return await self.httpx_request(url, method, **kwargs)
627
+ elif client.lower() == 'aiohttp':
628
+ return await self.aiohttp_request(url, method, **kwargs)
629
+ else:
630
+ raise ValueError(f"Unsupported client: {client}. Use 'httpx' or 'aiohttp'")
631
+
632
+ # Convenience methods
633
+ async def get(self, url: str, client: str = 'httpx', **kwargs):
634
+ """GET request."""
635
+ return await self.request(url, 'GET', client, **kwargs)
636
+
637
+ async def post(self, url: str, client: str = 'httpx', **kwargs):
638
+ """POST request."""
639
+ return await self.request(url, 'POST', client, **kwargs)
640
+
641
+ async def put(self, url: str, client: str = 'httpx', **kwargs):
642
+ """PUT request."""
643
+ return await self.request(url, 'PUT', client, **kwargs)
644
+
645
+ async def delete(self, url: str, client: str = 'httpx', **kwargs):
646
+ """DELETE request."""
647
+ return await self.request(url, 'DELETE', client, **kwargs)
648
+
649
+ async def patch(self, url: str, client: str = 'httpx', **kwargs):
650
+ """PATCH request."""
651
+ return await self.request(url, 'PATCH', client, **kwargs)
File without changes
@@ -0,0 +1,24 @@
1
+ """
2
+ Image processing and generation interfaces for Parrot.
3
+ """
4
+ from .yolo import YOLOPlugin
5
+ from .vision import VisionTransformerPlugin
6
+ from .hash import ImageHashPlugin
7
+ from .abstract import ImagePlugin
8
+ from .exif import EXIFPlugin
9
+ from .zerodetect import ZeroShotDetectionPlugin
10
+ from .classify import ClassificationPlugin
11
+ from .detect import DetectionPlugin
12
+ from .analisys import AnalysisPlugin
13
+
14
+
15
+ PLUGINS = {
16
+ "exif": EXIFPlugin,
17
+ "hash": ImageHashPlugin,
18
+ "yolo": YOLOPlugin,
19
+ "vectorization": VisionTransformerPlugin,
20
+ "zeroshot": ZeroShotDetectionPlugin,
21
+ "classification": ClassificationPlugin,
22
+ 'detection': DetectionPlugin,
23
+ 'analysis': AnalysisPlugin
24
+ }