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.
- agentui/.prettierrc +15 -0
- agentui/QUICKSTART.md +272 -0
- agentui/README.md +59 -0
- agentui/env.example +16 -0
- agentui/jsconfig.json +14 -0
- agentui/package-lock.json +4242 -0
- agentui/package.json +34 -0
- agentui/scripts/postinstall/apply-patches.mjs +260 -0
- agentui/src/app.css +61 -0
- agentui/src/app.d.ts +13 -0
- agentui/src/app.html +12 -0
- agentui/src/components/LoadingSpinner.svelte +64 -0
- agentui/src/components/ThemeSwitcher.svelte +159 -0
- agentui/src/components/index.js +4 -0
- agentui/src/lib/api/bots.ts +60 -0
- agentui/src/lib/api/chat.ts +22 -0
- agentui/src/lib/api/http.ts +25 -0
- agentui/src/lib/components/BotCard.svelte +33 -0
- agentui/src/lib/components/ChatBubble.svelte +63 -0
- agentui/src/lib/components/Toast.svelte +21 -0
- agentui/src/lib/config.ts +20 -0
- agentui/src/lib/stores/auth.svelte.ts +73 -0
- agentui/src/lib/stores/theme.svelte.js +64 -0
- agentui/src/lib/stores/toast.svelte.ts +31 -0
- agentui/src/lib/utils/conversation.ts +39 -0
- agentui/src/routes/+layout.svelte +20 -0
- agentui/src/routes/+page.svelte +232 -0
- agentui/src/routes/login/+page.svelte +200 -0
- agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
- agentui/src/routes/talk/[agentId]/+page.ts +7 -0
- agentui/static/README.md +1 -0
- agentui/svelte.config.js +11 -0
- agentui/tailwind.config.ts +53 -0
- agentui/tsconfig.json +3 -0
- agentui/vite.config.ts +10 -0
- ai_parrot-0.17.2.dist-info/METADATA +472 -0
- ai_parrot-0.17.2.dist-info/RECORD +535 -0
- ai_parrot-0.17.2.dist-info/WHEEL +6 -0
- ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
- ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
- ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
- crew-builder/.prettierrc +15 -0
- crew-builder/QUICKSTART.md +259 -0
- crew-builder/README.md +113 -0
- crew-builder/env.example +17 -0
- crew-builder/jsconfig.json +14 -0
- crew-builder/package-lock.json +4182 -0
- crew-builder/package.json +37 -0
- crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
- crew-builder/src/app.css +62 -0
- crew-builder/src/app.d.ts +13 -0
- crew-builder/src/app.html +12 -0
- crew-builder/src/components/LoadingSpinner.svelte +64 -0
- crew-builder/src/components/ThemeSwitcher.svelte +149 -0
- crew-builder/src/components/index.js +9 -0
- crew-builder/src/lib/api/bots.ts +60 -0
- crew-builder/src/lib/api/chat.ts +80 -0
- crew-builder/src/lib/api/client.ts +56 -0
- crew-builder/src/lib/api/crew/crew.ts +136 -0
- crew-builder/src/lib/api/index.ts +5 -0
- crew-builder/src/lib/api/o365/auth.ts +65 -0
- crew-builder/src/lib/auth/auth.ts +54 -0
- crew-builder/src/lib/components/AgentNode.svelte +43 -0
- crew-builder/src/lib/components/BotCard.svelte +33 -0
- crew-builder/src/lib/components/ChatBubble.svelte +67 -0
- crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
- crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
- crew-builder/src/lib/components/JsonViewer.svelte +24 -0
- crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
- crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
- crew-builder/src/lib/components/Toast.svelte +67 -0
- crew-builder/src/lib/components/Toolbar.svelte +157 -0
- crew-builder/src/lib/components/index.ts +10 -0
- crew-builder/src/lib/config.ts +8 -0
- crew-builder/src/lib/stores/auth.svelte.ts +228 -0
- crew-builder/src/lib/stores/crewStore.ts +369 -0
- crew-builder/src/lib/stores/theme.svelte.js +145 -0
- crew-builder/src/lib/stores/toast.svelte.ts +69 -0
- crew-builder/src/lib/utils/conversation.ts +39 -0
- crew-builder/src/lib/utils/markdown.ts +122 -0
- crew-builder/src/lib/utils/talkHistory.ts +47 -0
- crew-builder/src/routes/+layout.svelte +20 -0
- crew-builder/src/routes/+page.svelte +539 -0
- crew-builder/src/routes/agents/+page.svelte +247 -0
- crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
- crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
- crew-builder/src/routes/builder/+page.svelte +204 -0
- crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
- crew-builder/src/routes/crew/ask/+page.ts +1 -0
- crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
- crew-builder/src/routes/login/+page.svelte +197 -0
- crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
- crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
- crew-builder/static/README.md +1 -0
- crew-builder/svelte.config.js +11 -0
- crew-builder/tailwind.config.ts +53 -0
- crew-builder/tsconfig.json +3 -0
- crew-builder/vite.config.ts +10 -0
- mcp_servers/calculator_server.py +309 -0
- parrot/__init__.py +27 -0
- parrot/__pycache__/__init__.cpython-310.pyc +0 -0
- parrot/__pycache__/version.cpython-310.pyc +0 -0
- parrot/_version.py +34 -0
- parrot/a2a/__init__.py +48 -0
- parrot/a2a/client.py +658 -0
- parrot/a2a/discovery.py +89 -0
- parrot/a2a/mixin.py +257 -0
- parrot/a2a/models.py +376 -0
- parrot/a2a/server.py +770 -0
- parrot/agents/__init__.py +29 -0
- parrot/bots/__init__.py +12 -0
- parrot/bots/a2a_agent.py +19 -0
- parrot/bots/abstract.py +3139 -0
- parrot/bots/agent.py +1129 -0
- parrot/bots/basic.py +9 -0
- parrot/bots/chatbot.py +669 -0
- parrot/bots/data.py +1618 -0
- parrot/bots/database/__init__.py +5 -0
- parrot/bots/database/abstract.py +3071 -0
- parrot/bots/database/cache.py +286 -0
- parrot/bots/database/models.py +468 -0
- parrot/bots/database/prompts.py +154 -0
- parrot/bots/database/retries.py +98 -0
- parrot/bots/database/router.py +269 -0
- parrot/bots/database/sql.py +41 -0
- parrot/bots/db/__init__.py +6 -0
- parrot/bots/db/abstract.py +556 -0
- parrot/bots/db/bigquery.py +602 -0
- parrot/bots/db/cache.py +85 -0
- parrot/bots/db/documentdb.py +668 -0
- parrot/bots/db/elastic.py +1014 -0
- parrot/bots/db/influx.py +898 -0
- parrot/bots/db/mock.py +96 -0
- parrot/bots/db/multi.py +783 -0
- parrot/bots/db/prompts.py +185 -0
- parrot/bots/db/sql.py +1255 -0
- parrot/bots/db/tools.py +212 -0
- parrot/bots/document.py +680 -0
- parrot/bots/hrbot.py +15 -0
- parrot/bots/kb.py +170 -0
- parrot/bots/mcp.py +36 -0
- parrot/bots/orchestration/README.md +463 -0
- parrot/bots/orchestration/__init__.py +1 -0
- parrot/bots/orchestration/agent.py +155 -0
- parrot/bots/orchestration/crew.py +3330 -0
- parrot/bots/orchestration/fsm.py +1179 -0
- parrot/bots/orchestration/hr.py +434 -0
- parrot/bots/orchestration/storage/__init__.py +4 -0
- parrot/bots/orchestration/storage/memory.py +100 -0
- parrot/bots/orchestration/storage/mixin.py +119 -0
- parrot/bots/orchestration/verify.py +202 -0
- parrot/bots/product.py +204 -0
- parrot/bots/prompts/__init__.py +96 -0
- parrot/bots/prompts/agents.py +155 -0
- parrot/bots/prompts/data.py +216 -0
- parrot/bots/prompts/output_generation.py +8 -0
- parrot/bots/scraper/__init__.py +3 -0
- parrot/bots/scraper/models.py +122 -0
- parrot/bots/scraper/scraper.py +1173 -0
- parrot/bots/scraper/templates.py +115 -0
- parrot/bots/stores/__init__.py +5 -0
- parrot/bots/stores/local.py +172 -0
- parrot/bots/webdev.py +81 -0
- parrot/cli.py +17 -0
- parrot/clients/__init__.py +16 -0
- parrot/clients/base.py +1491 -0
- parrot/clients/claude.py +1191 -0
- parrot/clients/factory.py +129 -0
- parrot/clients/google.py +4567 -0
- parrot/clients/gpt.py +1975 -0
- parrot/clients/grok.py +432 -0
- parrot/clients/groq.py +986 -0
- parrot/clients/hf.py +582 -0
- parrot/clients/models.py +18 -0
- parrot/conf.py +395 -0
- parrot/embeddings/__init__.py +9 -0
- parrot/embeddings/base.py +157 -0
- parrot/embeddings/google.py +98 -0
- parrot/embeddings/huggingface.py +74 -0
- parrot/embeddings/openai.py +84 -0
- parrot/embeddings/processor.py +88 -0
- parrot/exceptions.c +13868 -0
- parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/exceptions.pxd +22 -0
- parrot/exceptions.pxi +15 -0
- parrot/exceptions.pyx +44 -0
- parrot/generators/__init__.py +29 -0
- parrot/generators/base.py +200 -0
- parrot/generators/html.py +293 -0
- parrot/generators/react.py +205 -0
- parrot/generators/streamlit.py +203 -0
- parrot/generators/template.py +105 -0
- parrot/handlers/__init__.py +4 -0
- parrot/handlers/agent.py +861 -0
- parrot/handlers/agents/__init__.py +1 -0
- parrot/handlers/agents/abstract.py +900 -0
- parrot/handlers/bots.py +338 -0
- parrot/handlers/chat.py +915 -0
- parrot/handlers/creation.sql +192 -0
- parrot/handlers/crew/ARCHITECTURE.md +362 -0
- parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
- parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
- parrot/handlers/crew/__init__.py +0 -0
- parrot/handlers/crew/handler.py +801 -0
- parrot/handlers/crew/models.py +229 -0
- parrot/handlers/crew/redis_persistence.py +523 -0
- parrot/handlers/jobs/__init__.py +10 -0
- parrot/handlers/jobs/job.py +384 -0
- parrot/handlers/jobs/mixin.py +627 -0
- parrot/handlers/jobs/models.py +115 -0
- parrot/handlers/jobs/worker.py +31 -0
- parrot/handlers/models.py +596 -0
- parrot/handlers/o365_auth.py +105 -0
- parrot/handlers/stream.py +337 -0
- parrot/interfaces/__init__.py +6 -0
- parrot/interfaces/aws.py +143 -0
- parrot/interfaces/credentials.py +113 -0
- parrot/interfaces/database.py +27 -0
- parrot/interfaces/google.py +1123 -0
- parrot/interfaces/hierarchy.py +1227 -0
- parrot/interfaces/http.py +651 -0
- parrot/interfaces/images/__init__.py +0 -0
- parrot/interfaces/images/plugins/__init__.py +24 -0
- parrot/interfaces/images/plugins/abstract.py +58 -0
- parrot/interfaces/images/plugins/analisys.py +148 -0
- parrot/interfaces/images/plugins/classify.py +150 -0
- parrot/interfaces/images/plugins/classifybase.py +182 -0
- parrot/interfaces/images/plugins/detect.py +150 -0
- parrot/interfaces/images/plugins/exif.py +1103 -0
- parrot/interfaces/images/plugins/hash.py +52 -0
- parrot/interfaces/images/plugins/vision.py +104 -0
- parrot/interfaces/images/plugins/yolo.py +66 -0
- parrot/interfaces/images/plugins/zerodetect.py +197 -0
- parrot/interfaces/o365.py +978 -0
- parrot/interfaces/onedrive.py +822 -0
- parrot/interfaces/sharepoint.py +1435 -0
- parrot/interfaces/soap.py +257 -0
- parrot/loaders/__init__.py +8 -0
- parrot/loaders/abstract.py +1131 -0
- parrot/loaders/audio.py +199 -0
- parrot/loaders/basepdf.py +53 -0
- parrot/loaders/basevideo.py +1568 -0
- parrot/loaders/csv.py +409 -0
- parrot/loaders/docx.py +116 -0
- parrot/loaders/epubloader.py +316 -0
- parrot/loaders/excel.py +199 -0
- parrot/loaders/factory.py +55 -0
- parrot/loaders/files/__init__.py +0 -0
- parrot/loaders/files/abstract.py +39 -0
- parrot/loaders/files/html.py +26 -0
- parrot/loaders/files/text.py +63 -0
- parrot/loaders/html.py +152 -0
- parrot/loaders/markdown.py +442 -0
- parrot/loaders/pdf.py +373 -0
- parrot/loaders/pdfmark.py +320 -0
- parrot/loaders/pdftables.py +506 -0
- parrot/loaders/ppt.py +476 -0
- parrot/loaders/qa.py +63 -0
- parrot/loaders/splitters/__init__.py +10 -0
- parrot/loaders/splitters/base.py +138 -0
- parrot/loaders/splitters/md.py +228 -0
- parrot/loaders/splitters/token.py +143 -0
- parrot/loaders/txt.py +26 -0
- parrot/loaders/video.py +89 -0
- parrot/loaders/videolocal.py +218 -0
- parrot/loaders/videounderstanding.py +377 -0
- parrot/loaders/vimeo.py +167 -0
- parrot/loaders/web.py +599 -0
- parrot/loaders/youtube.py +504 -0
- parrot/manager/__init__.py +5 -0
- parrot/manager/manager.py +1030 -0
- parrot/mcp/__init__.py +28 -0
- parrot/mcp/adapter.py +105 -0
- parrot/mcp/cli.py +174 -0
- parrot/mcp/client.py +119 -0
- parrot/mcp/config.py +75 -0
- parrot/mcp/integration.py +842 -0
- parrot/mcp/oauth.py +933 -0
- parrot/mcp/server.py +225 -0
- parrot/mcp/transports/__init__.py +3 -0
- parrot/mcp/transports/base.py +279 -0
- parrot/mcp/transports/grpc_session.py +163 -0
- parrot/mcp/transports/http.py +312 -0
- parrot/mcp/transports/mcp.proto +108 -0
- parrot/mcp/transports/quic.py +1082 -0
- parrot/mcp/transports/sse.py +330 -0
- parrot/mcp/transports/stdio.py +309 -0
- parrot/mcp/transports/unix.py +395 -0
- parrot/mcp/transports/websocket.py +547 -0
- parrot/memory/__init__.py +16 -0
- parrot/memory/abstract.py +209 -0
- parrot/memory/agent.py +32 -0
- parrot/memory/cache.py +175 -0
- parrot/memory/core.py +555 -0
- parrot/memory/file.py +153 -0
- parrot/memory/mem.py +131 -0
- parrot/memory/redis.py +613 -0
- parrot/models/__init__.py +46 -0
- parrot/models/basic.py +118 -0
- parrot/models/compliance.py +208 -0
- parrot/models/crew.py +395 -0
- parrot/models/detections.py +654 -0
- parrot/models/generation.py +85 -0
- parrot/models/google.py +223 -0
- parrot/models/groq.py +23 -0
- parrot/models/openai.py +30 -0
- parrot/models/outputs.py +285 -0
- parrot/models/responses.py +938 -0
- parrot/notifications/__init__.py +743 -0
- parrot/openapi/__init__.py +3 -0
- parrot/openapi/components.yaml +641 -0
- parrot/openapi/config.py +322 -0
- parrot/outputs/__init__.py +32 -0
- parrot/outputs/formats/__init__.py +108 -0
- parrot/outputs/formats/altair.py +359 -0
- parrot/outputs/formats/application.py +122 -0
- parrot/outputs/formats/base.py +351 -0
- parrot/outputs/formats/bokeh.py +356 -0
- parrot/outputs/formats/card.py +424 -0
- parrot/outputs/formats/chart.py +436 -0
- parrot/outputs/formats/d3.py +255 -0
- parrot/outputs/formats/echarts.py +310 -0
- parrot/outputs/formats/generators/__init__.py +0 -0
- parrot/outputs/formats/generators/abstract.py +61 -0
- parrot/outputs/formats/generators/panel.py +145 -0
- parrot/outputs/formats/generators/streamlit.py +86 -0
- parrot/outputs/formats/generators/terminal.py +63 -0
- parrot/outputs/formats/holoviews.py +310 -0
- parrot/outputs/formats/html.py +147 -0
- parrot/outputs/formats/jinja2.py +46 -0
- parrot/outputs/formats/json.py +87 -0
- parrot/outputs/formats/map.py +933 -0
- parrot/outputs/formats/markdown.py +172 -0
- parrot/outputs/formats/matplotlib.py +237 -0
- parrot/outputs/formats/mixins/__init__.py +0 -0
- parrot/outputs/formats/mixins/emaps.py +855 -0
- parrot/outputs/formats/plotly.py +341 -0
- parrot/outputs/formats/seaborn.py +310 -0
- parrot/outputs/formats/table.py +397 -0
- parrot/outputs/formats/template_report.py +138 -0
- parrot/outputs/formats/yaml.py +125 -0
- parrot/outputs/formatter.py +152 -0
- parrot/outputs/templates/__init__.py +95 -0
- parrot/pipelines/__init__.py +0 -0
- parrot/pipelines/abstract.py +210 -0
- parrot/pipelines/detector.py +124 -0
- parrot/pipelines/models.py +90 -0
- parrot/pipelines/planogram.py +3002 -0
- parrot/pipelines/table.sql +97 -0
- parrot/plugins/__init__.py +106 -0
- parrot/plugins/importer.py +80 -0
- parrot/py.typed +0 -0
- parrot/registry/__init__.py +18 -0
- parrot/registry/registry.py +594 -0
- parrot/scheduler/__init__.py +1189 -0
- parrot/scheduler/models.py +60 -0
- parrot/security/__init__.py +16 -0
- parrot/security/prompt_injection.py +268 -0
- parrot/security/security_events.sql +25 -0
- parrot/services/__init__.py +1 -0
- parrot/services/mcp/__init__.py +8 -0
- parrot/services/mcp/config.py +13 -0
- parrot/services/mcp/server.py +295 -0
- parrot/services/o365_remote_auth.py +235 -0
- parrot/stores/__init__.py +7 -0
- parrot/stores/abstract.py +352 -0
- parrot/stores/arango.py +1090 -0
- parrot/stores/bigquery.py +1377 -0
- parrot/stores/cache.py +106 -0
- parrot/stores/empty.py +10 -0
- parrot/stores/faiss_store.py +1157 -0
- parrot/stores/kb/__init__.py +9 -0
- parrot/stores/kb/abstract.py +68 -0
- parrot/stores/kb/cache.py +165 -0
- parrot/stores/kb/doc.py +325 -0
- parrot/stores/kb/hierarchy.py +346 -0
- parrot/stores/kb/local.py +457 -0
- parrot/stores/kb/prompt.py +28 -0
- parrot/stores/kb/redis.py +659 -0
- parrot/stores/kb/store.py +115 -0
- parrot/stores/kb/user.py +374 -0
- parrot/stores/models.py +59 -0
- parrot/stores/pgvector.py +3 -0
- parrot/stores/postgres.py +2853 -0
- parrot/stores/utils/__init__.py +0 -0
- parrot/stores/utils/chunking.py +197 -0
- parrot/telemetry/__init__.py +3 -0
- parrot/telemetry/mixin.py +111 -0
- parrot/template/__init__.py +3 -0
- parrot/template/engine.py +259 -0
- parrot/tools/__init__.py +23 -0
- parrot/tools/abstract.py +644 -0
- parrot/tools/agent.py +363 -0
- parrot/tools/arangodbsearch.py +537 -0
- parrot/tools/arxiv_tool.py +188 -0
- parrot/tools/calculator/__init__.py +3 -0
- parrot/tools/calculator/operations/__init__.py +38 -0
- parrot/tools/calculator/operations/calculus.py +80 -0
- parrot/tools/calculator/operations/statistics.py +76 -0
- parrot/tools/calculator/tool.py +150 -0
- parrot/tools/cloudwatch.py +988 -0
- parrot/tools/codeinterpreter/__init__.py +127 -0
- parrot/tools/codeinterpreter/executor.py +371 -0
- parrot/tools/codeinterpreter/internals.py +473 -0
- parrot/tools/codeinterpreter/models.py +643 -0
- parrot/tools/codeinterpreter/prompts.py +224 -0
- parrot/tools/codeinterpreter/tool.py +664 -0
- parrot/tools/company_info/__init__.py +6 -0
- parrot/tools/company_info/tool.py +1138 -0
- parrot/tools/correlationanalysis.py +437 -0
- parrot/tools/database/abstract.py +286 -0
- parrot/tools/database/bq.py +115 -0
- parrot/tools/database/cache.py +284 -0
- parrot/tools/database/models.py +95 -0
- parrot/tools/database/pg.py +343 -0
- parrot/tools/databasequery.py +1159 -0
- parrot/tools/db.py +1800 -0
- parrot/tools/ddgo.py +370 -0
- parrot/tools/decorators.py +271 -0
- parrot/tools/dftohtml.py +282 -0
- parrot/tools/document.py +549 -0
- parrot/tools/ecs.py +819 -0
- parrot/tools/edareport.py +368 -0
- parrot/tools/elasticsearch.py +1049 -0
- parrot/tools/employees.py +462 -0
- parrot/tools/epson/__init__.py +96 -0
- parrot/tools/excel.py +683 -0
- parrot/tools/file/__init__.py +13 -0
- parrot/tools/file/abstract.py +76 -0
- parrot/tools/file/gcs.py +378 -0
- parrot/tools/file/local.py +284 -0
- parrot/tools/file/s3.py +511 -0
- parrot/tools/file/tmp.py +309 -0
- parrot/tools/file/tool.py +501 -0
- parrot/tools/file_reader.py +129 -0
- parrot/tools/flowtask/__init__.py +19 -0
- parrot/tools/flowtask/tool.py +761 -0
- parrot/tools/gittoolkit.py +508 -0
- parrot/tools/google/__init__.py +18 -0
- parrot/tools/google/base.py +169 -0
- parrot/tools/google/tools.py +1251 -0
- parrot/tools/googlelocation.py +5 -0
- parrot/tools/googleroutes.py +5 -0
- parrot/tools/googlesearch.py +5 -0
- parrot/tools/googlesitesearch.py +5 -0
- parrot/tools/googlevoice.py +2 -0
- parrot/tools/gvoice.py +695 -0
- parrot/tools/ibisworld/README.md +225 -0
- parrot/tools/ibisworld/__init__.py +11 -0
- parrot/tools/ibisworld/tool.py +366 -0
- parrot/tools/jiratoolkit.py +1718 -0
- parrot/tools/manager.py +1098 -0
- parrot/tools/math.py +152 -0
- parrot/tools/metadata.py +476 -0
- parrot/tools/msteams.py +1621 -0
- parrot/tools/msword.py +635 -0
- parrot/tools/multidb.py +580 -0
- parrot/tools/multistoresearch.py +369 -0
- parrot/tools/networkninja.py +167 -0
- parrot/tools/nextstop/__init__.py +4 -0
- parrot/tools/nextstop/base.py +286 -0
- parrot/tools/nextstop/employee.py +733 -0
- parrot/tools/nextstop/store.py +462 -0
- parrot/tools/notification.py +435 -0
- parrot/tools/o365/__init__.py +42 -0
- parrot/tools/o365/base.py +295 -0
- parrot/tools/o365/bundle.py +522 -0
- parrot/tools/o365/events.py +554 -0
- parrot/tools/o365/mail.py +992 -0
- parrot/tools/o365/onedrive.py +497 -0
- parrot/tools/o365/sharepoint.py +641 -0
- parrot/tools/openapi_toolkit.py +904 -0
- parrot/tools/openweather.py +527 -0
- parrot/tools/pdfprint.py +1001 -0
- parrot/tools/powerbi.py +518 -0
- parrot/tools/powerpoint.py +1113 -0
- parrot/tools/pricestool.py +146 -0
- parrot/tools/products/__init__.py +246 -0
- parrot/tools/prophet_tool.py +171 -0
- parrot/tools/pythonpandas.py +630 -0
- parrot/tools/pythonrepl.py +910 -0
- parrot/tools/qsource.py +436 -0
- parrot/tools/querytoolkit.py +395 -0
- parrot/tools/quickeda.py +827 -0
- parrot/tools/resttool.py +553 -0
- parrot/tools/retail/__init__.py +0 -0
- parrot/tools/retail/bby.py +528 -0
- parrot/tools/sandboxtool.py +703 -0
- parrot/tools/sassie/__init__.py +352 -0
- parrot/tools/scraping/__init__.py +7 -0
- parrot/tools/scraping/docs/select.md +466 -0
- parrot/tools/scraping/documentation.md +1278 -0
- parrot/tools/scraping/driver.py +436 -0
- parrot/tools/scraping/models.py +576 -0
- parrot/tools/scraping/options.py +85 -0
- parrot/tools/scraping/orchestrator.py +517 -0
- parrot/tools/scraping/readme.md +740 -0
- parrot/tools/scraping/tool.py +3115 -0
- parrot/tools/seasonaldetection.py +642 -0
- parrot/tools/shell_tool/__init__.py +5 -0
- parrot/tools/shell_tool/actions.py +408 -0
- parrot/tools/shell_tool/engine.py +155 -0
- parrot/tools/shell_tool/models.py +322 -0
- parrot/tools/shell_tool/tool.py +442 -0
- parrot/tools/site_search.py +214 -0
- parrot/tools/textfile.py +418 -0
- parrot/tools/think.py +378 -0
- parrot/tools/toolkit.py +298 -0
- parrot/tools/webapp_tool.py +187 -0
- parrot/tools/whatif.py +1279 -0
- parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
- parrot/tools/workday/__init__.py +6 -0
- parrot/tools/workday/models.py +1389 -0
- parrot/tools/workday/tool.py +1293 -0
- parrot/tools/yfinance_tool.py +306 -0
- parrot/tools/zipcode.py +217 -0
- parrot/utils/__init__.py +2 -0
- parrot/utils/helpers.py +73 -0
- parrot/utils/parsers/__init__.py +5 -0
- parrot/utils/parsers/toml.c +12078 -0
- parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/parsers/toml.pyx +21 -0
- parrot/utils/toml.py +11 -0
- parrot/utils/types.cpp +20936 -0
- parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
- parrot/utils/types.pyx +213 -0
- parrot/utils/uv.py +11 -0
- parrot/version.py +10 -0
- parrot/yaml-rs/Cargo.lock +350 -0
- parrot/yaml-rs/Cargo.toml +19 -0
- parrot/yaml-rs/pyproject.toml +19 -0
- parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
- parrot/yaml-rs/src/lib.rs +222 -0
- requirements/docker-compose.yml +24 -0
- 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
|