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,146 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
from datetime import date
|
|
4
|
+
from parrot.tools.querytoolkit import QueryToolkit
|
|
5
|
+
from parrot.tools.decorators import tool_schema
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PriceInput(BaseModel):
|
|
9
|
+
tenant: str = Field(..., description="Program or tenant identifier (e.g. hisense, epson, etc).")
|
|
10
|
+
model: str = Field(..., description="Model number of the product to query the price for.")
|
|
11
|
+
week: int = Field(..., description="Week number for which to retrieve the price.")
|
|
12
|
+
output_format: str = Field("structured", description="Output format: 'string' or 'structured'.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ModelPriceInput(BaseModel):
|
|
16
|
+
tenant: str = Field(..., description="Program or tenant identifier (e.g. hisense, epson, etc).")
|
|
17
|
+
model: str = Field(..., description="Model number of the product to query the price for.")
|
|
18
|
+
output_format: str = Field(default="pandas", description="Output format: 'string' or 'structured'.")
|
|
19
|
+
limit: int = Field(10, description="Number of records to retrieve.")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class WeeklyPriceInput(BaseModel):
|
|
23
|
+
tenant: str = Field(..., description="Program or tenant identifier (e.g. hisense, epson, etc).")
|
|
24
|
+
week: int = Field(..., description="Week number for which to retrieve the price.")
|
|
25
|
+
output_format: str = Field(default="pandas", description="Output format: 'string' or 'structured'.")
|
|
26
|
+
limit: int = Field(10, description="Number of records to retrieve.")
|
|
27
|
+
|
|
28
|
+
class TotalPriceInput(BaseModel):
|
|
29
|
+
tenant: str = Field(..., description="Program or tenant identifier (e.g. hisense, epson, etc).")
|
|
30
|
+
start_date: str = Field(..., description="Start date for the price query (YYYY-MM-DD).")
|
|
31
|
+
end_date: str = Field(..., description="End date for the price query (YYYY-MM-DD).")
|
|
32
|
+
output_format: str = Field(default="pandas", description="Output format: 'string' or 'structured'.")
|
|
33
|
+
limit: int = Field(50, description="Number of records to retrieve.")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PriceOutput(BaseModel):
|
|
37
|
+
product_id: str = Field(..., description="Unique identifier for the product.")
|
|
38
|
+
price: float = Field(..., description="Price of the product.")
|
|
39
|
+
week: int = Field(..., description="Week number for which the price is applicable.")
|
|
40
|
+
start_date: date = Field(..., description="Start date of the pricing period.")
|
|
41
|
+
end_date: date = Field(..., description="End date of the pricing period.")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class PricesTool(QueryToolkit):
|
|
46
|
+
"""Tool for querying product prices from a database or API."""
|
|
47
|
+
|
|
48
|
+
name = "prices_tool"
|
|
49
|
+
description = "A tool to query product prices."
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@tool_schema(PriceInput, description="The price information as a string.")
|
|
53
|
+
async def get_model_price(
|
|
54
|
+
self,
|
|
55
|
+
tenant: str,
|
|
56
|
+
model: str,
|
|
57
|
+
week: int,
|
|
58
|
+
output_format: str = "structured",
|
|
59
|
+
) -> PriceOutput:
|
|
60
|
+
"""Fetches the price of a product for a given tenant, model, and week.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
tenant (str): The program or tenant identifier.
|
|
64
|
+
model (str): The model number of the product.
|
|
65
|
+
week (int): The week number for which to retrieve the price.
|
|
66
|
+
"""
|
|
67
|
+
sql = await self._get_query("get_pricing")
|
|
68
|
+
sql = sql.format(
|
|
69
|
+
tenant=tenant,
|
|
70
|
+
model=model,
|
|
71
|
+
week=week
|
|
72
|
+
)
|
|
73
|
+
try:
|
|
74
|
+
return await self._get_dataset(
|
|
75
|
+
sql,
|
|
76
|
+
output_format=output_format,
|
|
77
|
+
structured_obj=PriceOutput if output_format == "structured" else None
|
|
78
|
+
)
|
|
79
|
+
except ValueError as ve:
|
|
80
|
+
return f"No Pricing data found for the specified tenant and product, error: {ve}"
|
|
81
|
+
except Exception as e:
|
|
82
|
+
return f"Error fetching pricing data: {e}"
|
|
83
|
+
|
|
84
|
+
@tool_schema(WeeklyPriceInput, description="The price information as a string.")
|
|
85
|
+
async def get_weekly_price(
|
|
86
|
+
self,
|
|
87
|
+
tenant: str,
|
|
88
|
+
week: int,
|
|
89
|
+
limit: int = 10,
|
|
90
|
+
output_format: str = "structured",
|
|
91
|
+
) -> List[PriceOutput]:
|
|
92
|
+
"""Fetches all product prices for a given tenant and week.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
tenant (str): The program or tenant identifier.
|
|
96
|
+
week (int): The week number for which to retrieve the price.
|
|
97
|
+
"""
|
|
98
|
+
sql = await self._get_query("get_weekly_pricing")
|
|
99
|
+
sql = sql.format(
|
|
100
|
+
tenant=tenant,
|
|
101
|
+
week=week,
|
|
102
|
+
limit=limit
|
|
103
|
+
)
|
|
104
|
+
try:
|
|
105
|
+
return await self._get_dataset(
|
|
106
|
+
sql,
|
|
107
|
+
output_format=output_format,
|
|
108
|
+
structured_obj=PriceOutput if output_format == "structured" else None
|
|
109
|
+
)
|
|
110
|
+
except ValueError as ve:
|
|
111
|
+
return f"No Pricing data found for the specified tenant and product, error: {ve}"
|
|
112
|
+
except Exception as e:
|
|
113
|
+
return f"Error fetching pricing data: {e}"
|
|
114
|
+
|
|
115
|
+
@tool_schema(TotalPriceInput, description="The price information as a string.")
|
|
116
|
+
async def get_price(
|
|
117
|
+
self,
|
|
118
|
+
tenant: str,
|
|
119
|
+
start_date: str,
|
|
120
|
+
end_date: str,
|
|
121
|
+
limit: int = 10,
|
|
122
|
+
output_format: str = "structured",
|
|
123
|
+
) -> List[PriceOutput]:
|
|
124
|
+
"""Fetches all product prices for a given tenant and date range.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
tenant (str): The program or tenant identifier.
|
|
128
|
+
week (int): The week number for which to retrieve the price.
|
|
129
|
+
"""
|
|
130
|
+
sql = await self._get_query("get_total_pricing")
|
|
131
|
+
sql = sql.format(
|
|
132
|
+
tenant=tenant,
|
|
133
|
+
start_date=start_date,
|
|
134
|
+
end_date=end_date,
|
|
135
|
+
limit=limit
|
|
136
|
+
)
|
|
137
|
+
try:
|
|
138
|
+
return await self._get_dataset(
|
|
139
|
+
sql,
|
|
140
|
+
output_format=output_format,
|
|
141
|
+
structured_obj=PriceOutput if output_format == "structured" else None
|
|
142
|
+
)
|
|
143
|
+
except ValueError as ve:
|
|
144
|
+
return f"No Pricing data found for the specified tenant and product, error: {ve}"
|
|
145
|
+
except Exception as e:
|
|
146
|
+
return f"Error fetching pricing data: {e}"
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
from typing import Optional, Dict, Union, List, Any
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import json
|
|
4
|
+
from pydantic import BaseModel, Field, field_validator, ConfigDict
|
|
5
|
+
from navconfig import BASE_DIR
|
|
6
|
+
from asyncdb import AsyncDB
|
|
7
|
+
from asyncdb.models import Model, Field as ModelField
|
|
8
|
+
from querysource.conf import default_dsn
|
|
9
|
+
from ..abstract import AbstractTool
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ProductInput(BaseModel):
|
|
14
|
+
"""Input schema for product information requests."""
|
|
15
|
+
model: str = Field(
|
|
16
|
+
..., description="The product model identifier (e.g., 'X1234', 'Y5678')."
|
|
17
|
+
)
|
|
18
|
+
program_slug: str = Field(
|
|
19
|
+
..., description="The program slug associated with the product (e.g., 'alpha', 'beta')."
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ProductInfo(BaseModel):
|
|
24
|
+
"""Schema for the product information returned by the query."""
|
|
25
|
+
name: str
|
|
26
|
+
model: str
|
|
27
|
+
description: str
|
|
28
|
+
picture_url: str
|
|
29
|
+
brand: str
|
|
30
|
+
# pricing: Decimal
|
|
31
|
+
pricing: str
|
|
32
|
+
customer_satisfaction: Optional[str] = None
|
|
33
|
+
product_evaluation: Optional[str] = None
|
|
34
|
+
product_compliant: Optional[str] = None
|
|
35
|
+
# specifications: Dict[str, Union[dict, list]] = Field(
|
|
36
|
+
# default_factory=dict,
|
|
37
|
+
# description="Specifications of the product, can be a dict or list."
|
|
38
|
+
# )
|
|
39
|
+
specifications: Dict[str, Union[str, int, float, bool, list, dict]] = Field(
|
|
40
|
+
default_factory=dict,
|
|
41
|
+
description="Specifications of the product as a dictionary."
|
|
42
|
+
)
|
|
43
|
+
review_average: float
|
|
44
|
+
reviews: int
|
|
45
|
+
|
|
46
|
+
@field_validator('specifications', mode='before')
|
|
47
|
+
@classmethod
|
|
48
|
+
def parse_specifications(cls, v):
|
|
49
|
+
if v is None or v == '':
|
|
50
|
+
return {}
|
|
51
|
+
if isinstance(v, dict):
|
|
52
|
+
return v
|
|
53
|
+
if isinstance(v, (bytes, bytearray)):
|
|
54
|
+
v = v.decode('utf-8', errors='ignore')
|
|
55
|
+
if isinstance(v, str):
|
|
56
|
+
try:
|
|
57
|
+
parsed = json.loads(v)
|
|
58
|
+
except json.JSONDecodeError as e:
|
|
59
|
+
raise ValueError("Specifications field is not a valid JSON string.") from e
|
|
60
|
+
if not isinstance(parsed, dict):
|
|
61
|
+
raise TypeError("Specifications JSON must decode to a dictionary.")
|
|
62
|
+
return parsed
|
|
63
|
+
raise TypeError("specifications must be a dict or a JSON string.")
|
|
64
|
+
|
|
65
|
+
# Add a model_config to prevent additional properties
|
|
66
|
+
model_config = ConfigDict(
|
|
67
|
+
arbitrary_types_allowed=False,
|
|
68
|
+
extra="forbid",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
class ProductInfoTool(AbstractTool):
|
|
72
|
+
"""Tool to get detailed information about a specific product model."""
|
|
73
|
+
name = "get_product_information"
|
|
74
|
+
description = (
|
|
75
|
+
"Use this tool to get detailed information about a specific product model. "
|
|
76
|
+
"Provide the exact model identifier as input."
|
|
77
|
+
)
|
|
78
|
+
args_schema = ProductInput
|
|
79
|
+
|
|
80
|
+
async def _execute(self, model: str, program_slug: str) -> ProductInfo:
|
|
81
|
+
db = AsyncDB('pg', dsn=default_dsn)
|
|
82
|
+
|
|
83
|
+
# Use static_dir if configured, otherwise fall back to BASE_DIR
|
|
84
|
+
base_path = self.static_dir if hasattr(self, 'static_dir') and self.static_dir else BASE_DIR
|
|
85
|
+
|
|
86
|
+
# Try multiple paths for backward compatibility
|
|
87
|
+
# 1. Direct path (for when static_dir points to programs/ or taskstore/programs/)
|
|
88
|
+
query_file = base_path / program_slug / 'sql' / 'products.sql'
|
|
89
|
+
|
|
90
|
+
# 2. Try with 'programs/' prefix (for when static_dir points to base directory)
|
|
91
|
+
if not query_file.exists():
|
|
92
|
+
query_file = base_path / 'programs' / program_slug / 'sql' / 'products.sql'
|
|
93
|
+
|
|
94
|
+
# 3. Fallback to old structure: agents/product_report/{program_slug}/products.sql
|
|
95
|
+
if not query_file.exists():
|
|
96
|
+
query_file = base_path / 'agents' / 'product_report' / program_slug / 'products.sql'
|
|
97
|
+
|
|
98
|
+
if not query_file.exists():
|
|
99
|
+
raise FileNotFoundError(
|
|
100
|
+
f"Query file not found for program_slug '{program_slug}'. Tried:\n"
|
|
101
|
+
f" - {base_path / program_slug / 'sql' / 'products.sql'}\n"
|
|
102
|
+
f" - {base_path / 'programs' / program_slug / 'sql' / 'products.sql'}\n"
|
|
103
|
+
f" - {base_path / 'agents' / 'product_report' / program_slug / 'products.sql'}"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
query = query_file.read_text()
|
|
107
|
+
async with await db.connection() as conn: # noqa
|
|
108
|
+
product_data, error = await conn.query(query, model)
|
|
109
|
+
if error:
|
|
110
|
+
raise RuntimeError(f"Database query failed: {error}")
|
|
111
|
+
if not product_data:
|
|
112
|
+
raise ValueError(f"No product found with model '{model}' in program '{program_slug}'.")
|
|
113
|
+
|
|
114
|
+
return ProductInfo(**product_data[0])
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class ProductListInput(BaseModel):
|
|
118
|
+
"""Input schema for product list requests."""
|
|
119
|
+
program_slug: str = Field(
|
|
120
|
+
..., description="The program slug to get products from (e.g., 'google', 'hisense')."
|
|
121
|
+
)
|
|
122
|
+
models: Optional[List[str]] = Field(
|
|
123
|
+
default=None, description="Optional list of specific models to get. If None, gets all models."
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ProductListTool(AbstractTool):
|
|
128
|
+
"""Tool to get list of products for a given program/tenant."""
|
|
129
|
+
name = "get_products_list"
|
|
130
|
+
description = (
|
|
131
|
+
"Use this tool to get a list of products for a given program/tenant. "
|
|
132
|
+
"Provide the program slug as input. Optionally provide a list of specific models."
|
|
133
|
+
)
|
|
134
|
+
args_schema = ProductListInput
|
|
135
|
+
|
|
136
|
+
async def _execute(self, program_slug: str, models: Optional[List[str]] = None) -> List[Dict[str, str]]:
|
|
137
|
+
"""Get list of products for a program."""
|
|
138
|
+
db = AsyncDB('pg', dsn=default_dsn)
|
|
139
|
+
|
|
140
|
+
# Use static_dir if configured, otherwise fall back to BASE_DIR
|
|
141
|
+
base_path = self.static_dir if hasattr(self, 'static_dir') and self.static_dir else BASE_DIR
|
|
142
|
+
|
|
143
|
+
# Determine which SQL file to use
|
|
144
|
+
file_type = 'product_single.sql' if models else 'products_list.sql'
|
|
145
|
+
|
|
146
|
+
# Try multiple paths for backward compatibility
|
|
147
|
+
# 1. Direct path (for when static_dir points to programs/ or taskstore/programs/)
|
|
148
|
+
query_file = base_path / program_slug / 'sql' / file_type
|
|
149
|
+
|
|
150
|
+
# 2. Try with 'programs/' prefix (for when static_dir points to base directory)
|
|
151
|
+
if not query_file.exists():
|
|
152
|
+
query_file = base_path / 'programs' / program_slug / 'sql' / file_type
|
|
153
|
+
|
|
154
|
+
# 3. Fallback to old structure: agents/product_report/{program_slug}/<file>
|
|
155
|
+
if not query_file.exists():
|
|
156
|
+
query_file = base_path / 'agents' / 'product_report' / program_slug / file_type
|
|
157
|
+
|
|
158
|
+
if not query_file.exists():
|
|
159
|
+
raise FileNotFoundError(
|
|
160
|
+
f"Products query file not found for program_slug '{program_slug}'. Tried:\n"
|
|
161
|
+
f" - {base_path / program_slug / 'sql' / file_type}\n"
|
|
162
|
+
f" - {base_path / 'programs' / program_slug / 'sql' / file_type}\n"
|
|
163
|
+
f" - {base_path / 'agents' / 'product_report' / program_slug / file_type}"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
query = query_file.read_text()
|
|
167
|
+
async with await db.connection() as conn: # noqa
|
|
168
|
+
if models:
|
|
169
|
+
# Execute with models parameter
|
|
170
|
+
products, error = await conn.query(query, models)
|
|
171
|
+
else:
|
|
172
|
+
# Execute without parameters
|
|
173
|
+
products, error = await conn.query(query)
|
|
174
|
+
|
|
175
|
+
if error:
|
|
176
|
+
raise RuntimeError(f"Database query failed: {error}")
|
|
177
|
+
if not products:
|
|
178
|
+
return []
|
|
179
|
+
|
|
180
|
+
return products
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
class ProductResponse(Model):
|
|
184
|
+
"""
|
|
185
|
+
ProductResponse is a model that defines the structure of the response for Product agents.
|
|
186
|
+
"""
|
|
187
|
+
model: Optional[str] = ModelField(
|
|
188
|
+
default=None,
|
|
189
|
+
description="Model of the product"
|
|
190
|
+
)
|
|
191
|
+
program_slug: Optional[str] = ModelField(
|
|
192
|
+
default=None,
|
|
193
|
+
description="Program/tenant identifier"
|
|
194
|
+
)
|
|
195
|
+
agent_id: Optional[str] = ModelField(
|
|
196
|
+
default=None,
|
|
197
|
+
description="Unique identifier for the agent that processed the request"
|
|
198
|
+
)
|
|
199
|
+
agent_name: Optional[str] = ModelField(
|
|
200
|
+
default="ProductReport",
|
|
201
|
+
description="Name of the agent that processed the request"
|
|
202
|
+
)
|
|
203
|
+
status: str = ModelField(default="success", description="Status of the response")
|
|
204
|
+
data: Optional[str] = ModelField(
|
|
205
|
+
default=None,
|
|
206
|
+
description="Data returned by the agent, can be text, JSON, etc."
|
|
207
|
+
)
|
|
208
|
+
# Optional output field for structured data
|
|
209
|
+
output: Optional[Any] = ModelField(
|
|
210
|
+
default=None,
|
|
211
|
+
description="Output of the agent's processing"
|
|
212
|
+
)
|
|
213
|
+
attributes: Dict[str, str] = ModelField(
|
|
214
|
+
default_factory=dict,
|
|
215
|
+
description="Attributes associated with the response"
|
|
216
|
+
)
|
|
217
|
+
# Timestamp
|
|
218
|
+
created_at: datetime = ModelField(
|
|
219
|
+
default_factory=datetime.now, description="Timestamp when response was created"
|
|
220
|
+
)
|
|
221
|
+
# Optional file paths
|
|
222
|
+
transcript: Optional[str] = ModelField(
|
|
223
|
+
default=None, description="Transcript of the conversation with the agent"
|
|
224
|
+
)
|
|
225
|
+
script_path: Optional[str] = ModelField(
|
|
226
|
+
default=None, description="Path to the conversational script associated with the session"
|
|
227
|
+
)
|
|
228
|
+
podcast_path: Optional[str] = ModelField(
|
|
229
|
+
default=None, description="Path to the podcast associated with the session"
|
|
230
|
+
)
|
|
231
|
+
pdf_path: Optional[str] = ModelField(
|
|
232
|
+
default=None, description="Path to the PDF associated with the session"
|
|
233
|
+
)
|
|
234
|
+
document_path: Optional[str] = ModelField(
|
|
235
|
+
default=None, description="Path to any document generated during session"
|
|
236
|
+
)
|
|
237
|
+
# complete list of generated files:
|
|
238
|
+
files: List[str] = ModelField(
|
|
239
|
+
default_factory=list, description="List of documents generated during the session")
|
|
240
|
+
|
|
241
|
+
class Meta:
|
|
242
|
+
"""Meta class for ProductResponse."""
|
|
243
|
+
name = "products_informations"
|
|
244
|
+
schema = "product_report"
|
|
245
|
+
strict = True
|
|
246
|
+
frozen = False
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ProphetForecastTool for time series forecasting using Facebook Prophet.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, Dict, Optional
|
|
11
|
+
|
|
12
|
+
import pandas as pd
|
|
13
|
+
from pydantic import Field, field_validator
|
|
14
|
+
from prophet import Prophet
|
|
15
|
+
|
|
16
|
+
from .abstract import AbstractTool, AbstractToolArgsSchema, ToolResult
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ProphetForecastArgs(AbstractToolArgsSchema):
|
|
20
|
+
"""Arguments for :class:`ProphetForecastTool`."""
|
|
21
|
+
|
|
22
|
+
dataframe: str = Field(
|
|
23
|
+
..., description="Name or alias of the DataFrame containing the time series"
|
|
24
|
+
)
|
|
25
|
+
ds_column: str = Field(
|
|
26
|
+
..., description="Column with datestamp values (will be converted to datetime)"
|
|
27
|
+
)
|
|
28
|
+
y_column: str = Field(
|
|
29
|
+
..., description="Numeric column to forecast"
|
|
30
|
+
)
|
|
31
|
+
periods: int = Field(
|
|
32
|
+
365,
|
|
33
|
+
description="Number of future periods to forecast",
|
|
34
|
+
ge=1,
|
|
35
|
+
)
|
|
36
|
+
freq: str = Field(
|
|
37
|
+
"D",
|
|
38
|
+
description="Pandas frequency string for future periods (e.g., 'D', 'W', 'M')",
|
|
39
|
+
min_length=1,
|
|
40
|
+
)
|
|
41
|
+
include_history: bool = Field(
|
|
42
|
+
default=True,
|
|
43
|
+
description="Whether to include the historical dates in the forecast output",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@field_validator("periods")
|
|
47
|
+
@classmethod
|
|
48
|
+
def validate_periods(cls, value: int) -> int:
|
|
49
|
+
if value < 1:
|
|
50
|
+
raise ValueError("periods must be a positive integer")
|
|
51
|
+
return value
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ProphetForecastTool(AbstractTool):
|
|
55
|
+
"""Generate time series forecasts with Facebook Prophet and return plots."""
|
|
56
|
+
|
|
57
|
+
name = "prophet_forecast"
|
|
58
|
+
description = (
|
|
59
|
+
"Fit a Facebook Prophet model on a DataFrame and generate future forecasts "
|
|
60
|
+
"with corresponding forecast plot image."
|
|
61
|
+
)
|
|
62
|
+
args_schema = ProphetForecastArgs
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
dataframes: Optional[Dict[str, pd.DataFrame]] = None,
|
|
67
|
+
alias_map: Optional[Dict[str, str]] = None,
|
|
68
|
+
**kwargs: Any,
|
|
69
|
+
) -> None:
|
|
70
|
+
super().__init__(**kwargs)
|
|
71
|
+
self.dataframes: Dict[str, pd.DataFrame] = dataframes or {}
|
|
72
|
+
self.alias_map: Dict[str, str] = alias_map or {}
|
|
73
|
+
|
|
74
|
+
def _default_output_dir(self) -> Path:
|
|
75
|
+
return self.static_dir / "reports" / "prophet_forecast"
|
|
76
|
+
|
|
77
|
+
def update_context(
|
|
78
|
+
self, dataframes: Dict[str, pd.DataFrame], alias_map: Optional[Dict[str, str]] = None
|
|
79
|
+
) -> None:
|
|
80
|
+
"""Update internal references to available DataFrames and aliases."""
|
|
81
|
+
|
|
82
|
+
self.dataframes = dataframes
|
|
83
|
+
if alias_map is not None:
|
|
84
|
+
self.alias_map = alias_map
|
|
85
|
+
|
|
86
|
+
async def _execute(self, **kwargs: Any) -> ToolResult:
|
|
87
|
+
args = self.args_schema(**kwargs)
|
|
88
|
+
loop = asyncio.get_running_loop()
|
|
89
|
+
return await loop.run_in_executor(None, self._run_forecast, args)
|
|
90
|
+
|
|
91
|
+
def _run_forecast(self, args: ProphetForecastArgs) -> ToolResult:
|
|
92
|
+
dataframe = self._resolve_dataframe(args.dataframe)
|
|
93
|
+
cleaned_df = self._prepare_dataframe(dataframe, args)
|
|
94
|
+
|
|
95
|
+
model = Prophet()
|
|
96
|
+
model.fit(cleaned_df)
|
|
97
|
+
|
|
98
|
+
future = model.make_future_dataframe(
|
|
99
|
+
periods=args.periods, freq=args.freq, include_history=args.include_history
|
|
100
|
+
)
|
|
101
|
+
forecast = model.predict(future)
|
|
102
|
+
|
|
103
|
+
plot_path = self._save_forecast_plot(model, forecast, args.dataframe)
|
|
104
|
+
|
|
105
|
+
return ToolResult(
|
|
106
|
+
status="success",
|
|
107
|
+
result={
|
|
108
|
+
"forecast": forecast.to_dict(orient="records"),
|
|
109
|
+
"forecast_columns": list(forecast.columns),
|
|
110
|
+
"future_dataframe": future.to_dict(orient="records"),
|
|
111
|
+
"figure_path": str(plot_path),
|
|
112
|
+
"figure_url": self.to_static_url(plot_path),
|
|
113
|
+
},
|
|
114
|
+
metadata={
|
|
115
|
+
"dataframe": args.dataframe,
|
|
116
|
+
"ds_column": args.ds_column,
|
|
117
|
+
"y_column": args.y_column,
|
|
118
|
+
"periods": args.periods,
|
|
119
|
+
"freq": args.freq,
|
|
120
|
+
},
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def _resolve_dataframe(self, name: str) -> pd.DataFrame:
|
|
124
|
+
if name in self.dataframes:
|
|
125
|
+
return self.dataframes[name]
|
|
126
|
+
|
|
127
|
+
# Accept alias names (df1, df2, etc.)
|
|
128
|
+
for df_name, alias in self.alias_map.items():
|
|
129
|
+
if name == alias and df_name in self.dataframes:
|
|
130
|
+
return self.dataframes[df_name]
|
|
131
|
+
|
|
132
|
+
raise ValueError(
|
|
133
|
+
f"DataFrame '{name}' not found. Available: {list(self.dataframes.keys())}"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def _prepare_dataframe(
|
|
137
|
+
self, dataframe: pd.DataFrame, args: ProphetForecastArgs
|
|
138
|
+
) -> pd.DataFrame:
|
|
139
|
+
if args.ds_column not in dataframe.columns:
|
|
140
|
+
raise ValueError(
|
|
141
|
+
f"Date column '{args.ds_column}' not found. Columns: {list(dataframe.columns)}"
|
|
142
|
+
)
|
|
143
|
+
if args.y_column not in dataframe.columns:
|
|
144
|
+
raise ValueError(
|
|
145
|
+
f"Target column '{args.y_column}' not found. Columns: {list(dataframe.columns)}"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
df = dataframe.copy()
|
|
149
|
+
df[args.ds_column] = pd.to_datetime(df[args.ds_column], errors="coerce")
|
|
150
|
+
df[args.y_column] = pd.to_numeric(df[args.y_column], errors="coerce")
|
|
151
|
+
|
|
152
|
+
df = df.dropna(subset=[args.ds_column, args.y_column])
|
|
153
|
+
if df.empty:
|
|
154
|
+
raise ValueError("No valid rows available after cleaning for Prophet forecast")
|
|
155
|
+
|
|
156
|
+
df = df[[args.ds_column, args.y_column]].rename(
|
|
157
|
+
columns={args.ds_column: "ds", args.y_column: "y"}
|
|
158
|
+
)
|
|
159
|
+
return df
|
|
160
|
+
|
|
161
|
+
def _save_forecast_plot(
|
|
162
|
+
self, model: Prophet, forecast: pd.DataFrame, dataframe_name: str
|
|
163
|
+
) -> Path:
|
|
164
|
+
plot_dir = self.output_dir
|
|
165
|
+
plot_dir.mkdir(parents=True, exist_ok=True)
|
|
166
|
+
|
|
167
|
+
fig = model.plot(forecast)
|
|
168
|
+
filename = f"prophet_forecast_{dataframe_name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
|
|
169
|
+
file_path = plot_dir / filename
|
|
170
|
+
fig.savefig(file_path, bbox_inches="tight")
|
|
171
|
+
return file_path
|