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,642 @@
1
+ """
2
+ SeasonalDetectionTool for detecting stationarity in time series data using ADF and KPSS tests.
3
+ """
4
+ from typing import Dict, Any, Optional, List
5
+ from pathlib import Path
6
+ from datetime import datetime
7
+ import pandas as pd
8
+ import numpy as np
9
+ import matplotlib.pyplot as plt
10
+ import seaborn as sns
11
+ from pydantic import BaseModel, Field, field_validator
12
+ # Statistical tests
13
+ from statsmodels.tsa.stattools import adfuller, kpss
14
+ from statsmodels.tsa.seasonal import seasonal_decompose
15
+ from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
16
+ from .abstract import AbstractTool
17
+
18
+
19
+ class SeasonalDetectionArgs(BaseModel):
20
+ """Arguments schema for SeasonalDetectionTool."""
21
+
22
+ dataframe: Any = Field(
23
+ description="Pandas DataFrame containing the time series data"
24
+ )
25
+ title: str = Field(
26
+ description="Title for the analysis report"
27
+ )
28
+ time_column: str = Field(
29
+ description="Name of the column containing time series data (dates/timestamps)"
30
+ )
31
+ value_column: str = Field(
32
+ description="Name of the column containing the values to analyze for stationarity"
33
+ )
34
+ confidence_level: float = Field(
35
+ default=0.05,
36
+ description="Significance level for statistical tests (default: 0.05 for 95% confidence)"
37
+ )
38
+ maxlag: Optional[int] = Field(
39
+ default=None,
40
+ description="Maximum lag for ADF test. If None, automatically determined"
41
+ )
42
+ regression: str = Field(
43
+ default='c',
44
+ description="Regression type for ADF test: 'c' (constant), 'ct' (constant+trend), 'ctt' (constant+trend+trend^2), 'n' (no constant)"
45
+ )
46
+ nlags: Optional[int] = Field(
47
+ default=None,
48
+ description="Number of lags for KPSS test. If None, automatically determined"
49
+ )
50
+ generate_plots: bool = Field(
51
+ default=True,
52
+ description="Whether to generate visualization plots"
53
+ )
54
+ perform_decomposition: bool = Field(
55
+ default=True,
56
+ description="Whether to perform seasonal decomposition analysis"
57
+ )
58
+ remove_trend: bool = Field(
59
+ default=False,
60
+ description="Whether to test stationarity after detrending the data"
61
+ )
62
+ filename: Optional[str] = Field(
63
+ default=None,
64
+ description="Optional filename prefix for saving plots (without extension)"
65
+ )
66
+
67
+ @field_validator('confidence_level')
68
+ @classmethod
69
+ def validate_confidence_level(cls, v):
70
+ if not 0 < v < 1:
71
+ raise ValueError("Confidence level must be between 0 and 1")
72
+ return v
73
+
74
+ @field_validator('regression')
75
+ @classmethod
76
+ def validate_regression(cls, v):
77
+ valid_types = ['c', 'ct', 'ctt', 'n']
78
+ if v not in valid_types:
79
+ raise ValueError(f"Regression type must be one of {valid_types}")
80
+ return v
81
+
82
+
83
+ class SeasonalDetectionTool(AbstractTool):
84
+ """
85
+ Tool for detecting stationarity and seasonality in time series data.
86
+
87
+ This tool performs comprehensive stationarity analysis using:
88
+ - Augmented Dickey-Fuller (ADF) test
89
+ - Kwiatkowski-Phillips-Schmidt-Shin (KPSS) test
90
+ - Visual inspection through plots
91
+ - Optional seasonal decomposition
92
+ - Trend removal and re-testing
93
+
94
+ The tool helps determine if a time series is stationary (suitable for many
95
+ time series models) or non-stationary (requiring differencing or detrending).
96
+ """
97
+
98
+ name = "seasonal_detection"
99
+ description = "Analyze time series data for stationarity using ADF and KPSS statistical tests"
100
+ args_schema = SeasonalDetectionArgs
101
+
102
+ def __init__(self, **kwargs):
103
+ """Initialize the SeasonalDetectionTool."""
104
+ super().__init__(**kwargs)
105
+ self.results = {}
106
+
107
+ def _default_output_dir(self) -> Path:
108
+ """Get the default output directory for seasonal detection outputs."""
109
+ return self.static_dir / "reports" / "seasonal_detection"
110
+
111
+ def _validate_dataframe(self, df: Any) -> pd.DataFrame:
112
+ """
113
+ Validate that the input is a valid pandas DataFrame.
114
+
115
+ Args:
116
+ df: Input data to validate
117
+
118
+ Returns:
119
+ pandas DataFrame
120
+
121
+ Raises:
122
+ ValueError: If input is not a valid DataFrame
123
+ """
124
+ if not isinstance(df, pd.DataFrame):
125
+ raise ValueError("Input must be a pandas DataFrame")
126
+
127
+ if df.empty:
128
+ raise ValueError("DataFrame is empty")
129
+
130
+ return df
131
+
132
+ def _validate_columns(self, df: pd.DataFrame, time_col: str, value_col: str) -> None:
133
+ """
134
+ Validate that required columns exist in the DataFrame.
135
+
136
+ Args:
137
+ df: DataFrame to validate
138
+ time_col: Time column name
139
+ value_col: Value column name
140
+
141
+ Raises:
142
+ ValueError: If columns are missing or invalid
143
+ """
144
+ if time_col not in df.columns:
145
+ raise ValueError(f"Time column '{time_col}' not found. Available columns: {list(df.columns)}")
146
+
147
+ if value_col not in df.columns:
148
+ raise ValueError(f"Value column '{value_col}' not found. Available columns: {list(df.columns)}")
149
+
150
+ # Check if time column can be converted to datetime
151
+ try:
152
+ pd.to_datetime(df[time_col])
153
+ except Exception as e:
154
+ raise ValueError(f"Time column '{time_col}' cannot be converted to datetime: {e}")
155
+
156
+ # Check if value column is numeric
157
+ if not pd.api.types.is_numeric_dtype(df[value_col]):
158
+ try:
159
+ pd.to_numeric(df[value_col])
160
+ except Exception as e:
161
+ raise ValueError(f"Value column '{value_col}' is not numeric and cannot be converted: {e}")
162
+
163
+ def _prepare_time_series(self, df: pd.DataFrame, time_col: str, value_col: str) -> pd.Series:
164
+ """
165
+ Prepare time series data for analysis.
166
+
167
+ Args:
168
+ df: Input DataFrame
169
+ time_col: Time column name
170
+ value_col: Value column name
171
+
172
+ Returns:
173
+ Time series with datetime index
174
+ """
175
+ # Create a copy to avoid modifying original data
176
+ df_copy = df.copy()
177
+
178
+ # Convert time column to datetime
179
+ df_copy[time_col] = pd.to_datetime(df_copy[time_col])
180
+
181
+ # Convert value column to numeric
182
+ df_copy[value_col] = pd.to_numeric(df_copy[value_col], errors='coerce')
183
+
184
+ # Remove rows with NaN values
185
+ df_copy = df_copy.dropna(subset=[time_col, value_col])
186
+
187
+ if len(df_copy) == 0:
188
+ raise ValueError("No valid data points after cleaning")
189
+
190
+ # Sort by time
191
+ df_copy = df_copy.sort_values(time_col)
192
+
193
+ # Set time as index and return the series
194
+ df_copy.set_index(time_col, inplace=True)
195
+
196
+ return df_copy[value_col]
197
+
198
+ def _perform_adf_test(self, series: pd.Series, maxlag: Optional[int], regression: str) -> Dict[str, Any]:
199
+ """
200
+ Perform Augmented Dickey-Fuller test.
201
+
202
+ Args:
203
+ series: Time series data
204
+ maxlag: Maximum lag for test
205
+ regression: Regression type
206
+
207
+ Returns:
208
+ Dictionary with test results
209
+ """
210
+ try:
211
+ # Perform ADF test
212
+ adf_result = adfuller(series.dropna(), maxlag=maxlag, regression=regression)
213
+
214
+ # Extract results
215
+ adf_statistic = adf_result[0]
216
+ adf_pvalue = adf_result[1]
217
+ adf_lags_used = adf_result[2]
218
+ adf_nobs = adf_result[3]
219
+ adf_critical_values = adf_result[4]
220
+
221
+ # Determine stationarity based on p-value
222
+ is_stationary_pvalue = adf_pvalue <= 0.05
223
+
224
+ # Also check against critical values
225
+ is_stationary_critical = adf_statistic < adf_critical_values['5%']
226
+
227
+ return {
228
+ 'test_name': 'Augmented Dickey-Fuller',
229
+ 'statistic': adf_statistic,
230
+ 'p_value': adf_pvalue,
231
+ 'lags_used': adf_lags_used,
232
+ 'n_observations': adf_nobs,
233
+ 'critical_values': adf_critical_values,
234
+ 'is_stationary_pvalue': is_stationary_pvalue,
235
+ 'is_stationary_critical': is_stationary_critical,
236
+ 'interpretation': {
237
+ 'null_hypothesis': 'Time series has a unit root (non-stationary)',
238
+ 'alternative_hypothesis': 'Time series is stationary',
239
+ 'conclusion_pvalue': 'Stationary' if is_stationary_pvalue else 'Non-stationary',
240
+ 'conclusion_critical': 'Stationary' if is_stationary_critical else 'Non-stationary',
241
+ 'confidence': f'{(1-0.05)*100}%'
242
+ }
243
+ }
244
+
245
+ except Exception as e:
246
+ return {
247
+ 'test_name': 'Augmented Dickey-Fuller',
248
+ 'error': str(e),
249
+ 'success': False
250
+ }
251
+
252
+ def _perform_kpss_test(self, series: pd.Series, nlags: Optional[int], regression: str) -> Dict[str, Any]:
253
+ """
254
+ Perform Kwiatkowski-Phillips-Schmidt-Shin test.
255
+
256
+ Args:
257
+ series: Time series data
258
+ nlags: Number of lags
259
+ regression: Regression type ('c' for level, 'ct' for trend)
260
+
261
+ Returns:
262
+ Dictionary with test results
263
+ """
264
+ try:
265
+ # KPSS regression parameter mapping
266
+ kpss_regression = 'c' if regression in ['c', 'n'] else 'ct'
267
+
268
+ # Perform KPSS test
269
+ kpss_result = kpss(series.dropna(), regression=kpss_regression, nlags=nlags)
270
+
271
+ # Extract results
272
+ kpss_statistic = kpss_result[0]
273
+ kpss_pvalue = kpss_result[1]
274
+ kpss_lags_used = kpss_result[2]
275
+ kpss_critical_values = kpss_result[3]
276
+
277
+ # Determine stationarity based on p-value (opposite of ADF)
278
+ is_stationary_pvalue = kpss_pvalue > 0.05
279
+
280
+ # Also check against critical values
281
+ is_stationary_critical = kpss_statistic < kpss_critical_values['5%']
282
+
283
+ return {
284
+ 'test_name': 'Kwiatkowski-Phillips-Schmidt-Shin',
285
+ 'statistic': kpss_statistic,
286
+ 'p_value': kpss_pvalue,
287
+ 'lags_used': kpss_lags_used,
288
+ 'critical_values': kpss_critical_values,
289
+ 'is_stationary_pvalue': is_stationary_pvalue,
290
+ 'is_stationary_critical': is_stationary_critical,
291
+ 'interpretation': {
292
+ 'null_hypothesis': 'Time series is stationary',
293
+ 'alternative_hypothesis': 'Time series has a unit root (non-stationary)',
294
+ 'conclusion_pvalue': 'Stationary' if is_stationary_pvalue else 'Non-stationary',
295
+ 'conclusion_critical': 'Stationary' if is_stationary_critical else 'Non-stationary',
296
+ 'confidence': f'{(1-0.05)*100}%'
297
+ }
298
+ }
299
+
300
+ except Exception as e:
301
+ return {
302
+ 'test_name': 'Kwiatkowski-Phillips-Schmidt-Shin',
303
+ 'error': str(e),
304
+ 'success': False
305
+ }
306
+
307
+ def _perform_seasonal_decomposition(self, series: pd.Series) -> Dict[str, Any]:
308
+ """
309
+ Perform seasonal decomposition analysis.
310
+
311
+ Args:
312
+ series: Time series data
313
+
314
+ Returns:
315
+ Dictionary with decomposition results
316
+ """
317
+ try:
318
+ # Determine frequency for decomposition
319
+ freq = self._infer_frequency(series)
320
+
321
+ if freq is None or freq < 4:
322
+ return {
323
+ 'success': False,
324
+ 'error': 'Cannot perform seasonal decomposition: insufficient data or unclear frequency'
325
+ }
326
+
327
+ # Perform decomposition
328
+ decomposition = seasonal_decompose(series, model='additive', period=freq)
329
+
330
+ # Calculate variance explained by each component
331
+ total_var = series.var()
332
+ trend_var = decomposition.trend.var()
333
+ seasonal_var = decomposition.seasonal.var()
334
+ residual_var = decomposition.resid.var()
335
+
336
+ return {
337
+ 'success': True,
338
+ 'frequency': freq,
339
+ 'trend_variance_explained': trend_var / total_var if total_var > 0 else 0,
340
+ 'seasonal_variance_explained': seasonal_var / total_var if total_var > 0 else 0,
341
+ 'residual_variance_explained': residual_var / total_var if total_var > 0 else 0,
342
+ 'decomposition': {
343
+ 'trend': decomposition.trend,
344
+ 'seasonal': decomposition.seasonal,
345
+ 'residual': decomposition.resid,
346
+ 'observed': decomposition.observed
347
+ }
348
+ }
349
+
350
+ except Exception as e:
351
+ return {
352
+ 'success': False,
353
+ 'error': str(e)
354
+ }
355
+
356
+ def _infer_frequency(self, series: pd.Series) -> Optional[int]:
357
+ """
358
+ Infer the frequency of the time series for seasonal decomposition.
359
+
360
+ Args:
361
+ series: Time series data
362
+
363
+ Returns:
364
+ Inferred frequency or None
365
+ """
366
+ try:
367
+ # Try to infer from pandas
368
+ if hasattr(series.index, 'freq') and series.index.freq:
369
+ return series.index.freq
370
+
371
+ # Estimate based on index differences
372
+ if len(series) < 4:
373
+ return None
374
+
375
+ # Calculate time differences
376
+ time_diffs = series.index.to_series().diff().dropna()
377
+ mode_diff = time_diffs.mode().iloc[0] if len(time_diffs.mode()) > 0 else None
378
+
379
+ if mode_diff is None:
380
+ return None
381
+
382
+ # Estimate frequency based on common patterns
383
+ days = mode_diff.total_seconds() / (24 * 3600)
384
+
385
+ if abs(days - 1) < 0.1: # Daily data
386
+ return 7 # Weekly seasonality
387
+ elif abs(days - 7) < 0.5: # Weekly data
388
+ return 52 # Annual seasonality
389
+ elif abs(days - 30.44) < 2: # Monthly data (approximate)
390
+ return 12 # Annual seasonality
391
+ elif abs(days - 91.31) < 5: # Quarterly data (approximate)
392
+ return 4 # Annual seasonality
393
+ else:
394
+ # Default to square root of length for other frequencies
395
+ return max(4, int(np.sqrt(len(series))))
396
+
397
+ except Exception:
398
+ return None
399
+
400
+ def _detrend_series(self, series: pd.Series) -> pd.Series:
401
+ """
402
+ Remove trend from time series using differencing.
403
+
404
+ Args:
405
+ series: Original time series
406
+
407
+ Returns:
408
+ Detrended time series
409
+ """
410
+ return series.diff().dropna()
411
+
412
+ def _create_visualizations(
413
+ self, series: pd.Series, results: Dict[str, Any], output_prefix: str
414
+ ) -> List[str]:
415
+ """
416
+ Create visualization plots for the analysis.
417
+
418
+ Args:
419
+ series: Time series data
420
+ results: Analysis results
421
+ output_prefix: Prefix for output files
422
+
423
+ Returns:
424
+ List of generated file paths
425
+ """
426
+ generated_files = []
427
+
428
+ try:
429
+ # Set up the plotting style
430
+ plt.style.use('seaborn-v0_8-whitegrid')
431
+ sns.set_palette("Set2")
432
+
433
+ # 1. Time series plot with summary
434
+ fig, axes = plt.subplots(2, 2, figsize=(15, 10))
435
+ fig.suptitle('Time Series Stationarity Analysis', fontsize=16, fontweight='bold')
436
+
437
+ # Original series
438
+ axes[0, 0].plot(series.index, series.values, linewidth=1.5, color='steelblue')
439
+ axes[0, 0].set_title('Original Time Series')
440
+ axes[0, 0].set_xlabel('Time')
441
+ axes[0, 0].set_ylabel('Value')
442
+ axes[0, 0].grid(True, alpha=0.3)
443
+
444
+ # Rolling statistics
445
+ rolling_mean = series.rolling(window=min(30, len(series)//4)).mean()
446
+ rolling_std = series.rolling(window=min(30, len(series)//4)).std()
447
+
448
+ axes[0, 1].plot(series.index, series.values, alpha=0.7, label='Original', color='steelblue')
449
+ axes[0, 1].plot(rolling_mean.index, rolling_mean.values, label='Rolling Mean', color='red', linewidth=2)
450
+ axes[0, 1].plot(rolling_std.index, rolling_std.values, label='Rolling Std', color='orange', linewidth=2)
451
+ axes[0, 1].set_title('Rolling Statistics')
452
+ axes[0, 1].legend()
453
+ axes[0, 1].grid(True, alpha=0.3)
454
+
455
+ # ACF and PACF plots
456
+ try:
457
+ plot_acf(series.dropna(), ax=axes[1, 0], lags=min(40, len(series)//4), title='Autocorrelation Function')
458
+ plot_pacf(series.dropna(), ax=axes[1, 1], lags=min(20, len(series)//8), title='Partial Autocorrelation Function')
459
+ except Exception as e:
460
+ axes[1, 0].text(0.5, 0.5, f'ACF plot error: {str(e)}', ha='center', va='center', transform=axes[1, 0].transAxes)
461
+ axes[1, 1].text(0.5, 0.5, f'PACF plot error: {str(e)}', ha='center', va='center', transform=axes[1, 1].transAxes)
462
+
463
+ plt.tight_layout()
464
+
465
+ # Save the main plot
466
+ main_plot_path = self.output_dir / f"{output_prefix}_stationarity_analysis.png"
467
+ plt.savefig(main_plot_path, dpi=300, bbox_inches='tight')
468
+ plt.close()
469
+ generated_files.append(str(main_plot_path))
470
+
471
+ # 2. Seasonal decomposition plot (if available)
472
+ if 'decomposition' in results and results['decomposition']['success']:
473
+ decomp_data = results['decomposition']['decomposition']
474
+
475
+ fig, axes = plt.subplots(4, 1, figsize=(15, 12))
476
+ fig.suptitle('Seasonal Decomposition', fontsize=16, fontweight='bold')
477
+
478
+ # Plot each component
479
+ components = [
480
+ ('Observed', decomp_data['observed'], 'steelblue'),
481
+ ('Trend', decomp_data['trend'], 'red'),
482
+ ('Seasonal', decomp_data['seasonal'], 'green'),
483
+ ('Residual', decomp_data['residual'], 'purple')
484
+ ]
485
+
486
+ for i, (name, data, color) in enumerate(components):
487
+ axes[i].plot(data.index, data.values, color=color, linewidth=1.5)
488
+ axes[i].set_title(f'{name} Component')
489
+ axes[i].grid(True, alpha=0.3)
490
+ if i == len(components) - 1:
491
+ axes[i].set_xlabel('Time')
492
+
493
+ plt.tight_layout()
494
+
495
+ # Save decomposition plot
496
+ decomp_plot_path = self.output_dir / f"{output_prefix}_seasonal_decomposition.png"
497
+ plt.savefig(decomp_plot_path, dpi=300, bbox_inches='tight')
498
+ plt.close()
499
+ generated_files.append(str(decomp_plot_path))
500
+
501
+ except Exception as e:
502
+ self.logger.error(f"Error creating visualizations: {e}")
503
+
504
+ return generated_files
505
+
506
+ async def _execute(self, **kwargs) -> Dict[str, Any]:
507
+ """
508
+ Execute the seasonal detection analysis.
509
+
510
+ Args:
511
+ **kwargs: Tool arguments
512
+
513
+ Returns:
514
+ Dictionary with analysis results
515
+ """
516
+ try:
517
+ # Extract arguments
518
+ dataframe = kwargs['dataframe']
519
+ title = kwargs['title']
520
+ time_column = kwargs['time_column']
521
+ value_column = kwargs['value_column']
522
+ confidence_level = kwargs.get('confidence_level', 0.05)
523
+ maxlag = kwargs.get('maxlag')
524
+ regression = kwargs.get('regression', 'c')
525
+ nlags = kwargs.get('nlags')
526
+ generate_plots = kwargs.get('generate_plots', True)
527
+ perform_decomposition = kwargs.get('perform_decomposition', True)
528
+ remove_trend = kwargs.get('remove_trend', False)
529
+ filename = kwargs.get('filename')
530
+
531
+ # Validate DataFrame
532
+ df = self._validate_dataframe(dataframe)
533
+
534
+ # Validate columns
535
+ self._validate_columns(df, time_column, value_column)
536
+
537
+ # Prepare time series
538
+ series = self._prepare_time_series(df, time_column, value_column)
539
+
540
+ self.logger.info(f"Analyzing time series with {len(series)} observations")
541
+
542
+ # Initialize results
543
+ analysis_results = {
544
+ 'data_info': {
545
+ 'dataframe_name': title,
546
+ 'time_column': time_column,
547
+ 'value_column': value_column,
548
+ 'n_observations': len(series),
549
+ 'date_range': {
550
+ 'start': str(series.index.min()),
551
+ 'end': str(series.index.max())
552
+ },
553
+ 'missing_values': series.isnull().sum(),
554
+ 'descriptive_stats': {
555
+ 'mean': float(series.mean()),
556
+ 'std': float(series.std()),
557
+ 'min': float(series.min()),
558
+ 'max': float(series.max()),
559
+ 'skewness': float(series.skew()),
560
+ 'kurtosis': float(series.kurtosis())
561
+ }
562
+ },
563
+ 'stationarity_tests': {},
564
+ 'overall_conclusion': {}
565
+ }
566
+
567
+ # Perform ADF test
568
+ self.logger.info("Performing Augmented Dickey-Fuller test...")
569
+ adf_results = self._perform_adf_test(series, maxlag, regression)
570
+ analysis_results['stationarity_tests']['adf'] = adf_results
571
+
572
+ # Perform KPSS test
573
+ self.logger.info("Performing KPSS test...")
574
+ kpss_results = self._perform_kpss_test(series, nlags, regression)
575
+ analysis_results['stationarity_tests']['kpss'] = kpss_results
576
+
577
+ # Perform seasonal decomposition if requested
578
+ if perform_decomposition:
579
+ self.logger.info("Performing seasonal decomposition...")
580
+ decomp_results = self._perform_seasonal_decomposition(series)
581
+ analysis_results['decomposition'] = decomp_results
582
+
583
+ # Test after detrending if requested
584
+ if remove_trend:
585
+ self.logger.info("Testing stationarity after detrending...")
586
+ detrended_series = self._detrend_series(series)
587
+
588
+ adf_detrended = self._perform_adf_test(detrended_series, maxlag, regression)
589
+ kpss_detrended = self._perform_kpss_test(detrended_series, nlags, regression)
590
+
591
+ analysis_results['detrended_tests'] = {
592
+ 'adf': adf_detrended,
593
+ 'kpss': kpss_detrended,
594
+ 'n_observations': len(detrended_series)
595
+ }
596
+
597
+ # Overall conclusion
598
+ adf_stationary = adf_results.get('is_stationary_pvalue', False)
599
+ kpss_stationary = kpss_results.get('is_stationary_pvalue', False)
600
+
601
+ if adf_stationary and kpss_stationary:
602
+ conclusion = "STATIONARY"
603
+ recommendation = "Both tests suggest the series is stationary. It's suitable for many time series models."
604
+ elif not adf_stationary and not kpss_stationary:
605
+ conclusion = "NON-STATIONARY"
606
+ recommendation = "Both tests suggest the series is non-stationary. Consider differencing or detrending."
607
+ else:
608
+ conclusion = "INCONCLUSIVE"
609
+ if adf_stationary:
610
+ recommendation = "ADF suggests stationary, KPSS suggests non-stationary. The series may be stationary around a deterministic trend."
611
+ else:
612
+ recommendation = "KPSS suggests stationary, ADF suggests non-stationary. This is unusual and may indicate issues with the data or test assumptions."
613
+
614
+ analysis_results['overall_conclusion'] = {
615
+ 'conclusion': conclusion,
616
+ 'recommendation': recommendation,
617
+ 'adf_stationary': adf_stationary,
618
+ 'kpss_stationary': kpss_stationary,
619
+ 'confidence_level': confidence_level
620
+ }
621
+
622
+ # Generate plots if requested
623
+ generated_files = []
624
+ if generate_plots:
625
+ self.logger.info("Generating visualization plots...")
626
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
627
+ output_prefix = f"{title}_{value_column}_{timestamp}"
628
+ generated_files = self._create_visualizations(series, analysis_results, output_prefix)
629
+
630
+ # Convert file paths to URLs
631
+ analysis_results['generated_files'] = {
632
+ 'file_paths': generated_files,
633
+ 'file_urls': [self.to_static_url(fp) for fp in generated_files]
634
+ }
635
+
636
+ self.logger.info(f"Seasonal detection analysis completed. Conclusion: {conclusion}")
637
+
638
+ return analysis_results
639
+
640
+ except Exception as e:
641
+ self.logger.error(f"Error in seasonal detection analysis: {e}")
642
+ raise
@@ -0,0 +1,5 @@
1
+ from .models import ShellToolArgs
2
+ from .tool import ShellTool
3
+
4
+
5
+ __all__ = ["ShellToolArgs", "ShellTool"]