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,733 @@
|
|
|
1
|
+
from typing import List, Dict, Any, Union, Optional
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from datetime import datetime, date, time
|
|
4
|
+
import json
|
|
5
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
6
|
+
from ...exceptions import ToolError # pylint: disable=E0611
|
|
7
|
+
from ..decorators import tool_schema
|
|
8
|
+
from .base import BaseNextStop
|
|
9
|
+
|
|
10
|
+
def today_date() -> date:
|
|
11
|
+
"""Returns today's date."""
|
|
12
|
+
return datetime.now().date()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class EmployeeInput(BaseModel):
|
|
16
|
+
"""Input for the employee-related operations in the NextStop tool."""
|
|
17
|
+
employee_id: Optional[str] = Field(
|
|
18
|
+
default=None,
|
|
19
|
+
description="Unique identifier for the employee"
|
|
20
|
+
)
|
|
21
|
+
program: Optional[str] = Field(
|
|
22
|
+
default=None,
|
|
23
|
+
description="Program slug for the store (e.g., 'hisense', 'epson')"
|
|
24
|
+
)
|
|
25
|
+
output_format: Optional[str] = Field(
|
|
26
|
+
default='structured',
|
|
27
|
+
description="Output format for the employee data"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
display_name: Optional[str] = Field(
|
|
31
|
+
default=None,
|
|
32
|
+
description="Name of the employee"
|
|
33
|
+
)
|
|
34
|
+
email: Optional[str] = Field(
|
|
35
|
+
default=None,
|
|
36
|
+
description="Email address of the employee"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Add a model_config to prevent additional properties
|
|
40
|
+
model_config = ConfigDict(
|
|
41
|
+
arbitrary_types_allowed=False,
|
|
42
|
+
extra="forbid",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
class ManagerInput(BaseModel):
|
|
46
|
+
"""Input for the manager-related operations in the NextStop tool."""
|
|
47
|
+
manager_id: str = Field(description="Unique identifier for the manager")
|
|
48
|
+
program: Optional[str] = Field(
|
|
49
|
+
default=None,
|
|
50
|
+
description="Program slug for the store (e.g., 'hisense', 'epson')"
|
|
51
|
+
)
|
|
52
|
+
output_format: Optional[str] = Field(
|
|
53
|
+
default='structured',
|
|
54
|
+
description="Output format for the employee data"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Add a model_config to prevent additional properties
|
|
58
|
+
model_config = ConfigDict(
|
|
59
|
+
arbitrary_types_allowed=False,
|
|
60
|
+
extra="forbid",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
## Outputs:
|
|
64
|
+
class VisitDetailed(BaseModel):
|
|
65
|
+
"""Detailed visit information model."""
|
|
66
|
+
visit_date: date = Field(..., description="Date of the visit")
|
|
67
|
+
column_name: str = Field(..., description="Column identifier for the data point")
|
|
68
|
+
store_id: str = Field(description="Store identifier")
|
|
69
|
+
question: str = Field(..., description="Question asked during the visit")
|
|
70
|
+
answer: Optional[str] = Field(None, description="Answer provided for the question")
|
|
71
|
+
account_name: str = Field(..., description="Name of the retail account/store")
|
|
72
|
+
visit_timestamp: Optional[datetime] = Field(default=None, description="Visit timestamp")
|
|
73
|
+
visit_length: Optional[float] = Field(default=None, description="Visit length")
|
|
74
|
+
time_in: Optional[time] = Field(default=None, description="Check-in time")
|
|
75
|
+
time_out: Optional[time] = Field(default=None, description="Check-out time")
|
|
76
|
+
|
|
77
|
+
@field_validator('question', mode='before')
|
|
78
|
+
@classmethod
|
|
79
|
+
def truncate_question(cls, v: str) -> str:
|
|
80
|
+
"""Truncate question if longer than 200 characters."""
|
|
81
|
+
if not isinstance(v, str):
|
|
82
|
+
return v
|
|
83
|
+
return v[:200] + " (...)" if len(v) > 200 else v
|
|
84
|
+
|
|
85
|
+
class VisitInformation(BaseModel):
|
|
86
|
+
"""Visit information model."""
|
|
87
|
+
# Basic visit info
|
|
88
|
+
visitor_name: str = Field(..., description="Name of the visitor/manager")
|
|
89
|
+
visitor_email: str = Field(..., description="Email address of the visitor")
|
|
90
|
+
visitor_name: Optional[str] = Field(default=None, description="Visitor name")
|
|
91
|
+
visitor_username: Optional[str] = Field(default=None, description="Visitor username")
|
|
92
|
+
# aggregated visit data
|
|
93
|
+
visit_data: Optional[List[VisitDetailed]] = Field(
|
|
94
|
+
default=None,
|
|
95
|
+
description="Visit data aggregated"
|
|
96
|
+
)
|
|
97
|
+
# Aggregated questions:
|
|
98
|
+
questions: Optional[Dict[str, List[Dict[str, Any]]]] = Field(
|
|
99
|
+
default=None,
|
|
100
|
+
description="Aggregated visit questions and answers organized by question type"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
class VisitDetail(BaseModel):
|
|
104
|
+
"""Individual visit detail from the visit_data JSONB array."""
|
|
105
|
+
visit_date: date = Field(..., description="Date of the visit")
|
|
106
|
+
column_name: str = Field(..., description="Column identifier for the data point")
|
|
107
|
+
question: str = Field(..., description="Question asked during the visit")
|
|
108
|
+
answer: Optional[str] = Field(None, description="Answer provided for the question")
|
|
109
|
+
account_name: str = Field(..., description="Name of the retail account/store")
|
|
110
|
+
visit_length: Optional[float] = Field(default=None, description="Visit length in minutes")
|
|
111
|
+
store_id: str = Field(..., description="Store identifier")
|
|
112
|
+
|
|
113
|
+
@field_validator('question', mode='before')
|
|
114
|
+
@classmethod
|
|
115
|
+
def truncate_question(cls, v: str) -> str:
|
|
116
|
+
"""Truncate question if longer than 200 characters."""
|
|
117
|
+
if not isinstance(v, str):
|
|
118
|
+
return v
|
|
119
|
+
|
|
120
|
+
max_length = 200
|
|
121
|
+
if len(v) > max_length:
|
|
122
|
+
# Truncate and add ellipsis
|
|
123
|
+
return v[:max_length-6] + " (...)"
|
|
124
|
+
|
|
125
|
+
return v
|
|
126
|
+
|
|
127
|
+
class VisitsByManager(BaseModel):
|
|
128
|
+
"""Individual record for visits by manager data"""
|
|
129
|
+
visitor_name: str = Field(..., description="Name of the visitor/manager")
|
|
130
|
+
visitor_email: str = Field(..., description="Email address of the visitor")
|
|
131
|
+
assigned_stores: Optional[int] = Field(default=0, description="Number of stores assigned to the manager")
|
|
132
|
+
total_visits: Optional[int] = Field(default=0, description="Total number of visits made")
|
|
133
|
+
visited_stores: int = Field(..., description="Number of stores actually visited")
|
|
134
|
+
visit_duration: float = Field(..., description="Total visit duration in minutes")
|
|
135
|
+
average_visit_duration: Optional[float] = Field(..., description="Average visit duration in minutes")
|
|
136
|
+
hour_of_visit: float = Field(..., description="Average hour of visit (24-hour format)")
|
|
137
|
+
current_visits: int = Field(..., description="Number of visits in current month")
|
|
138
|
+
previous_week_visits: Optional[int] = Field(default=0, description="Number of visits in previous week")
|
|
139
|
+
previous_month_visits: Optional[int] = Field(default=0, description="Number of visits in previous month's week")
|
|
140
|
+
most_frequent_day_of_week: int = Field(default=None, description="Most frequent day of week (0=Monday, 6=Sunday)")
|
|
141
|
+
most_frequent_store: str = Field(default=None, description="Most frequently visited store")
|
|
142
|
+
most_frequent_store_visits: Optional[float] = Field(default=None, description="Number of visits to the most frequent store")
|
|
143
|
+
visit_ratio: str = Field(..., description="Ratio of visited stores to assigned stores")
|
|
144
|
+
day_of_week: str = Field(..., description="Most frequent day name")
|
|
145
|
+
ranking_visits: int = Field(..., description="Current ranking by visits")
|
|
146
|
+
previous_week_ranking: int = Field(..., description="Previous week ranking by visits")
|
|
147
|
+
previous_month_ranking: int = Field(..., description="Previous month ranking by visits")
|
|
148
|
+
ranking_duration: int = Field(..., description="Ranking by visit duration")
|
|
149
|
+
|
|
150
|
+
class VisitsByManagerOutput(BaseModel):
|
|
151
|
+
"""Structured output for get_visits_by_manager tool"""
|
|
152
|
+
records: List[VisitsByManager] = Field(..., description="List of visitor stats")
|
|
153
|
+
total_records: int = Field(..., description="Total number of records returned")
|
|
154
|
+
generated_at: datetime = Field(default_factory=datetime.now, description="Timestamp when data was generated")
|
|
155
|
+
|
|
156
|
+
class Config:
|
|
157
|
+
json_encoders = {
|
|
158
|
+
datetime: lambda dt: dt.isoformat()
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class ManagerSales(BaseModel):
|
|
163
|
+
"""Individual record for Manager sales data"""
|
|
164
|
+
visitor_name: str = Field(..., description="Name of the employee/visitor")
|
|
165
|
+
visitor_email: str = Field(..., description="Email address of the employee")
|
|
166
|
+
total_sales: Optional[float] = Field(description="Total sales amount across all periods")
|
|
167
|
+
sales_current_week: Optional[float] = Field(description="Sales in current week")
|
|
168
|
+
sales_previous_week: Optional[float] = Field(description="Sales in previous week")
|
|
169
|
+
sales_previous_month: Optional[float] = Field(description="Sales from week of previous month")
|
|
170
|
+
current_ranking: Optional[int] = Field(description="Current ranking by sales performance")
|
|
171
|
+
previous_week_ranking: Optional[int] = Field(description="Previous month ranking by sales")
|
|
172
|
+
previous_month_ranking: Optional[int] = Field(description="Two months ago ranking by sales")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class ManagerSalesOutput(BaseModel):
|
|
176
|
+
"""Structured output for get_manager_sales tool"""
|
|
177
|
+
records: List[ManagerSales] = Field(..., description="List of manager sales")
|
|
178
|
+
total_records: int = Field(..., description="Total number of records returned")
|
|
179
|
+
generated_at: datetime = Field(default_factory=datetime.now, description="Timestamp when data was generated")
|
|
180
|
+
|
|
181
|
+
class Config:
|
|
182
|
+
json_encoders = {
|
|
183
|
+
datetime: lambda dt: dt.isoformat()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
class EmployeeSales(BaseModel):
|
|
187
|
+
"""Individual record for Employee sales data"""
|
|
188
|
+
store_id: str = Field(..., description="Store identifier")
|
|
189
|
+
tier: str = Field(..., description="Sales ranking")
|
|
190
|
+
sales_current_week: Optional[float] = Field(
|
|
191
|
+
...,
|
|
192
|
+
description="Sales in current week"
|
|
193
|
+
)
|
|
194
|
+
sales_previous_week: Optional[float] = Field(
|
|
195
|
+
...,
|
|
196
|
+
description="Sales in previous week"
|
|
197
|
+
)
|
|
198
|
+
week_over_week_delta: Optional[float] = Field(
|
|
199
|
+
..., description="Week over week sales delta"
|
|
200
|
+
)
|
|
201
|
+
week_over_week_variance: Optional[float] = Field(
|
|
202
|
+
..., description="Week over week sales variance"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class EmployeeVisit(BaseModel):
|
|
207
|
+
"""
|
|
208
|
+
Employee visit summary with aggregated statistics and detailed visit data.
|
|
209
|
+
|
|
210
|
+
This model represents the result of a complex SQL query that aggregates
|
|
211
|
+
employee visit data including timing patterns, visit counts, and detailed
|
|
212
|
+
visit information.
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
# Employee Information
|
|
216
|
+
visitor_name: str = Field(..., description="Name of the visiting employee")
|
|
217
|
+
visitor_email: str = Field(..., description="Email address of the visiting employee")
|
|
218
|
+
|
|
219
|
+
# Visit Statistics
|
|
220
|
+
latest_visit_date: date = Field(..., description="Date of the most recent visit")
|
|
221
|
+
number_of_visits: int = Field(..., ge=0, description="Total number of visits made")
|
|
222
|
+
latest_store_visited: Optional[str] = Field(
|
|
223
|
+
None,
|
|
224
|
+
description="Name of the most recently visited store"
|
|
225
|
+
)
|
|
226
|
+
# Unique stores visited
|
|
227
|
+
visited_stores: int = Field(..., ge=0, description="Number of unique stores visited")
|
|
228
|
+
|
|
229
|
+
# Time-based Metrics
|
|
230
|
+
visit_duration: Optional[float] = Field(
|
|
231
|
+
None,
|
|
232
|
+
ge=0,
|
|
233
|
+
description="Average visit duration in minutes"
|
|
234
|
+
)
|
|
235
|
+
average_hour_visit: Optional[float] = Field(
|
|
236
|
+
None,
|
|
237
|
+
ge=0,
|
|
238
|
+
le=23.99,
|
|
239
|
+
description="Average hour of day when visits occur (0-23.99)"
|
|
240
|
+
)
|
|
241
|
+
min_time_in: Optional[time] = Field(
|
|
242
|
+
None, description="Earliest check-in time across all visits"
|
|
243
|
+
)
|
|
244
|
+
max_time_out: Optional[time] = Field(
|
|
245
|
+
None, description="Latest check-out time across all visits"
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
# Pattern Analysis
|
|
249
|
+
most_frequent_hour_of_day: Optional[int] = Field(
|
|
250
|
+
None,
|
|
251
|
+
ge=0,
|
|
252
|
+
le=23,
|
|
253
|
+
description="Most common hour of day for visits (0-23)"
|
|
254
|
+
)
|
|
255
|
+
most_frequent_day_of_week: Optional[int] = Field(
|
|
256
|
+
None,
|
|
257
|
+
ge=0,
|
|
258
|
+
le=6,
|
|
259
|
+
description="Most common day of week for visits (0=Sunday, 6=Saturday)"
|
|
260
|
+
)
|
|
261
|
+
median_visit_duration: Optional[float] = Field(
|
|
262
|
+
None,
|
|
263
|
+
ge=0,
|
|
264
|
+
description="Median visit duration in minutes"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Detailed Visit Data
|
|
268
|
+
visit_data: List[VisitDetail] = Field(
|
|
269
|
+
default_factory=list,
|
|
270
|
+
description="Detailed information from each visit"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
# Retailer Summary
|
|
274
|
+
visited_retailers: Optional[Dict[str, int]] = Field(
|
|
275
|
+
None,
|
|
276
|
+
description="Dictionary mapping retailer names to visit counts"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Computed Properties
|
|
280
|
+
@property
|
|
281
|
+
def average_visits_per_store(self) -> Optional[float]:
|
|
282
|
+
"""Calculate average number of visits per store."""
|
|
283
|
+
if self.visited_stores > 0:
|
|
284
|
+
return round(self.number_of_visits / self.visited_stores, 2)
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def total_retailers(self) -> int:
|
|
289
|
+
"""Get total number of different retailers visited."""
|
|
290
|
+
return len(self.visited_retailers) if self.visited_retailers else 0
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def most_visited_retailer(self) -> Optional[str]:
|
|
294
|
+
"""Get the name of the most visited retailer."""
|
|
295
|
+
if self.visited_retailers:
|
|
296
|
+
return max(self.visited_retailers.items(), key=lambda x: x[1])[0]
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
@property
|
|
300
|
+
def day_of_week_name(self) -> Optional[str]:
|
|
301
|
+
"""Convert numeric day of week to name."""
|
|
302
|
+
if self.most_frequent_day_of_week is not None:
|
|
303
|
+
days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
|
304
|
+
return days[self.most_frequent_day_of_week]
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def visit_efficiency_score(self) -> Optional[float]:
|
|
309
|
+
"""
|
|
310
|
+
Calculate a visit efficiency score based on visit duration and store coverage.
|
|
311
|
+
Higher score indicates more efficient visits (shorter duration, more stores covered).
|
|
312
|
+
"""
|
|
313
|
+
if self.visit_duration and self.visited_stores > 0:
|
|
314
|
+
# Score: stores visited per minute of average visit time
|
|
315
|
+
return round(self.visited_stores / self.visit_duration, 4)
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
# Validators
|
|
319
|
+
@field_validator('visitor_email')
|
|
320
|
+
@classmethod
|
|
321
|
+
def validate_email_format(cls, v):
|
|
322
|
+
"""Basic email validation."""
|
|
323
|
+
if '@' not in v:
|
|
324
|
+
raise ValueError('Invalid email format')
|
|
325
|
+
return v.lower()
|
|
326
|
+
|
|
327
|
+
@field_validator('visit_data', mode='before')
|
|
328
|
+
@classmethod
|
|
329
|
+
def parse_visit_data(cls, v):
|
|
330
|
+
"""Parse visit data - handles lists directly from DataFrame."""
|
|
331
|
+
# If it's already a list of dicts (from DataFrame), process directly
|
|
332
|
+
if isinstance(v, list):
|
|
333
|
+
parsed_visits = []
|
|
334
|
+
for item in v:
|
|
335
|
+
if isinstance(item, dict):
|
|
336
|
+
try:
|
|
337
|
+
# Convert string dates to date objects if needed
|
|
338
|
+
if 'visit_date' in item and isinstance(item['visit_date'], str):
|
|
339
|
+
item['visit_date'] = datetime.strptime(item['visit_date'], '%Y-%m-%d').date()
|
|
340
|
+
|
|
341
|
+
parsed_visits.append(VisitDetail(**item))
|
|
342
|
+
except Exception as e:
|
|
343
|
+
# Log the error but continue processing other items
|
|
344
|
+
print(f"Error parsing visit detail: {e}, item: {item}")
|
|
345
|
+
continue
|
|
346
|
+
return parsed_visits
|
|
347
|
+
|
|
348
|
+
# Handle string JSON (shouldn't happen with DataFrame but just in case)
|
|
349
|
+
if isinstance(v, str):
|
|
350
|
+
import json
|
|
351
|
+
try:
|
|
352
|
+
v = json.loads(v)
|
|
353
|
+
# Recursive call with the parsed data
|
|
354
|
+
return cls.parse_visit_data(v)
|
|
355
|
+
except json.JSONDecodeError:
|
|
356
|
+
return []
|
|
357
|
+
|
|
358
|
+
# Return empty list for None or other types
|
|
359
|
+
return v or []
|
|
360
|
+
|
|
361
|
+
@field_validator('visited_retailers', mode='before')
|
|
362
|
+
@classmethod
|
|
363
|
+
def parse_visited_retailers(cls, v):
|
|
364
|
+
"""Parse visited retailers data if it comes as raw JSON."""
|
|
365
|
+
if isinstance(v, str):
|
|
366
|
+
import json
|
|
367
|
+
try:
|
|
368
|
+
v = json.loads(v)
|
|
369
|
+
except json.JSONDecodeError:
|
|
370
|
+
return {}
|
|
371
|
+
return v or {}
|
|
372
|
+
|
|
373
|
+
# Model validator for additional validation after all fields are processed
|
|
374
|
+
@model_validator(mode='after')
|
|
375
|
+
def validate_model(self):
|
|
376
|
+
"""Additional model-level validation."""
|
|
377
|
+
# Ensure visit counts make sense
|
|
378
|
+
if self.number_of_visits < 0:
|
|
379
|
+
raise ValueError("Number of visits cannot be negative")
|
|
380
|
+
|
|
381
|
+
if self.visited_stores > self.number_of_visits:
|
|
382
|
+
raise ValueError("Visited stores cannot exceed number of visits")
|
|
383
|
+
|
|
384
|
+
return self
|
|
385
|
+
|
|
386
|
+
class Config:
|
|
387
|
+
"""Pydantic configuration."""
|
|
388
|
+
# Allow extra fields that might come from the database
|
|
389
|
+
extra = "ignore"
|
|
390
|
+
# Use enum values in JSON serialization
|
|
391
|
+
use_enum_values = True
|
|
392
|
+
# Enable validation of assignment
|
|
393
|
+
validate_assignment = True
|
|
394
|
+
# Custom JSON encoders for special types
|
|
395
|
+
json_encoders = {
|
|
396
|
+
date: lambda v: v.isoformat(),
|
|
397
|
+
time: lambda v: v.isoformat(),
|
|
398
|
+
Decimal: lambda v: float(v)
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
def model_dump_summary(self) -> Dict[str, Any]:
|
|
402
|
+
"""
|
|
403
|
+
Return a summary version with key metrics only.
|
|
404
|
+
Useful for API responses where full detail isn't needed.
|
|
405
|
+
"""
|
|
406
|
+
return {
|
|
407
|
+
"visitor_name": self.visitor_name,
|
|
408
|
+
"visitor_email": self.visitor_email,
|
|
409
|
+
"latest_visit_date": self.latest_visit_date,
|
|
410
|
+
"number_of_visits": self.number_of_visits,
|
|
411
|
+
"visited_stores": self.visited_stores,
|
|
412
|
+
"visit_duration": self.visit_duration,
|
|
413
|
+
"most_visited_retailer": self.most_visited_retailer,
|
|
414
|
+
"total_retailers": self.total_retailers,
|
|
415
|
+
"visit_efficiency_score": self.visit_efficiency_score,
|
|
416
|
+
"day_of_week_name": self.day_of_week_name
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
def get_retailer_breakdown(self) -> List[Dict[str, Union[str, int]]]:
|
|
420
|
+
"""
|
|
421
|
+
Get a formatted breakdown of retailer visits.
|
|
422
|
+
Returns sorted list by visit count (descending).
|
|
423
|
+
"""
|
|
424
|
+
if not self.visited_retailers:
|
|
425
|
+
return []
|
|
426
|
+
|
|
427
|
+
return [
|
|
428
|
+
{"retailer": retailer, "visits": count}
|
|
429
|
+
for retailer, count in sorted(
|
|
430
|
+
self.visited_retailers.items(),
|
|
431
|
+
key=lambda x: x[1],
|
|
432
|
+
reverse=True
|
|
433
|
+
)
|
|
434
|
+
]
|
|
435
|
+
|
|
436
|
+
class EmployeeVisitCollection(BaseModel):
|
|
437
|
+
"""Collection of employee visits for batch operations."""
|
|
438
|
+
employees: List[EmployeeVisit] = Field(default_factory=list)
|
|
439
|
+
query_date_range: Optional[str] = Field(None, description="Date range of the query")
|
|
440
|
+
total_employees: int = Field(default=0, description="Total number of employees in results")
|
|
441
|
+
|
|
442
|
+
@property
|
|
443
|
+
def top_performers(self, limit: int = 5) -> List[EmployeeVisit]:
|
|
444
|
+
"""Get top performing employees by number of visits."""
|
|
445
|
+
return sorted(
|
|
446
|
+
self.employees,
|
|
447
|
+
key=lambda x: x.number_of_visits,
|
|
448
|
+
reverse=True
|
|
449
|
+
)[:limit]
|
|
450
|
+
|
|
451
|
+
@property
|
|
452
|
+
def most_efficient(self, limit: int = 5) -> List[EmployeeVisit]:
|
|
453
|
+
"""Get most efficient employees by visit efficiency score."""
|
|
454
|
+
efficient = [e for e in self.employees if e.visit_efficiency_score is not None]
|
|
455
|
+
return sorted(
|
|
456
|
+
efficient,
|
|
457
|
+
key=lambda x: x.visit_efficiency_score,
|
|
458
|
+
reverse=True
|
|
459
|
+
)[:limit]
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
# Example usage in your tool:
|
|
463
|
+
"""
|
|
464
|
+
async def get_employee_visits(employee_id: str) -> EmployeeVisit:
|
|
465
|
+
# Execute your SQL query
|
|
466
|
+
result = await db.fetch_one(sql)
|
|
467
|
+
|
|
468
|
+
# Create the EmployeeVisit instance
|
|
469
|
+
if result:
|
|
470
|
+
return EmployeeVisit(**dict(result))
|
|
471
|
+
else:
|
|
472
|
+
# Return empty result
|
|
473
|
+
return EmployeeVisit(
|
|
474
|
+
visitor_name="Unknown",
|
|
475
|
+
visitor_email=employee_id,
|
|
476
|
+
latest_visit_date=date.today(),
|
|
477
|
+
number_of_visits=0,
|
|
478
|
+
visited_stores=0
|
|
479
|
+
)
|
|
480
|
+
"""
|
|
481
|
+
|
|
482
|
+
class EmployeeToolkit(BaseNextStop):
|
|
483
|
+
"""Toolkit for managing employee-related operations in NextStop.
|
|
484
|
+
|
|
485
|
+
This toolkit provides tools to:
|
|
486
|
+
- employee_information: Get basic employee information.
|
|
487
|
+
- search_employee: Search for employees based on display name or email.
|
|
488
|
+
- get_by_employee_visits: Get visit information for a specific employee.
|
|
489
|
+
- get_visits_by_manager: Get visit information for a specific manager, including their employees.
|
|
490
|
+
- get_manager_sales: Fetch sales data for a specific manager and ranked performance.
|
|
491
|
+
- get_employee_sales: Fetch sales data for a specific employee.
|
|
492
|
+
"""
|
|
493
|
+
|
|
494
|
+
@tool_schema(ManagerInput)
|
|
495
|
+
async def get_visits_by_manager(
|
|
496
|
+
self,
|
|
497
|
+
manager_id: str,
|
|
498
|
+
program: str,
|
|
499
|
+
**kwargs
|
|
500
|
+
) -> List[VisitsByManager]:
|
|
501
|
+
"""Get Employee Visits data for a specific Manager, requires the associated_oid of the manager.
|
|
502
|
+
including total visits, average visit duration, and most frequent visit hours.
|
|
503
|
+
Useful for analyzing employee performance and visit patterns.
|
|
504
|
+
"""
|
|
505
|
+
if program:
|
|
506
|
+
self.program = program
|
|
507
|
+
sql = await self._get_query("visits_by_manager")
|
|
508
|
+
sql = sql.format(manager_id=manager_id)
|
|
509
|
+
try:
|
|
510
|
+
return await self._get_dataset(
|
|
511
|
+
sql,
|
|
512
|
+
output_format='structured',
|
|
513
|
+
structured_obj=VisitsByManager
|
|
514
|
+
)
|
|
515
|
+
except ToolError as te:
|
|
516
|
+
raise ValueError(
|
|
517
|
+
f"No Employee Visit data found for manager {manager_id}, error: {te}"
|
|
518
|
+
)
|
|
519
|
+
except ValueError as ve:
|
|
520
|
+
raise ValueError(f"Invalid data format, error: {ve}")
|
|
521
|
+
except Exception as e:
|
|
522
|
+
raise ValueError(f"Error fetching employee visit data: {e}")
|
|
523
|
+
|
|
524
|
+
@tool_schema(ManagerInput)
|
|
525
|
+
async def get_manager_sales(
|
|
526
|
+
self,
|
|
527
|
+
manager_id: str,
|
|
528
|
+
program: str,
|
|
529
|
+
**kwargs
|
|
530
|
+
) -> List[ManagerSales]:
|
|
531
|
+
"""Get Sales and goals for all employees related to a Manager.
|
|
532
|
+
Returns a ranked list of employees based on their sales performance.
|
|
533
|
+
Useful for understanding employee performance and sales distribution.
|
|
534
|
+
"""
|
|
535
|
+
if program:
|
|
536
|
+
self.program = program
|
|
537
|
+
sql = await self._get_query("manager_sales")
|
|
538
|
+
if not manager_id:
|
|
539
|
+
manager_id = kwargs.get('email')
|
|
540
|
+
if not manager_id:
|
|
541
|
+
raise ToolError("Manager ID is required to fetch employee sales data.")
|
|
542
|
+
sql = sql.format(manager_id=manager_id)
|
|
543
|
+
try:
|
|
544
|
+
return await self._get_dataset(
|
|
545
|
+
sql,
|
|
546
|
+
output_format='structured',
|
|
547
|
+
structured_obj=ManagerSales
|
|
548
|
+
)
|
|
549
|
+
except ToolError as te:
|
|
550
|
+
raise ValueError(f"No Sales data found for manager {manager_id}, error: {te}")
|
|
551
|
+
except ValueError as ve:
|
|
552
|
+
raise ValueError(f"Invalid data format, error: {ve}")
|
|
553
|
+
except Exception as e:
|
|
554
|
+
raise ValueError(f"Error fetching employee sales data: {e}")
|
|
555
|
+
|
|
556
|
+
@tool_schema(EmployeeInput)
|
|
557
|
+
async def employee_information(
|
|
558
|
+
self,
|
|
559
|
+
employee_id: str = None,
|
|
560
|
+
display_name: str = None,
|
|
561
|
+
email: str = None
|
|
562
|
+
) -> str:
|
|
563
|
+
"""Get basic information about an employee by their ID, display name or email.
|
|
564
|
+
Returns the employee's display name and email.
|
|
565
|
+
Useful for identifying employees in the system.
|
|
566
|
+
"""
|
|
567
|
+
conditions = []
|
|
568
|
+
if employee_id:
|
|
569
|
+
conditions.append(f"associate_oid = '{employee_id}'")
|
|
570
|
+
if display_name:
|
|
571
|
+
conditions.append(f"display_name = '{display_name}'")
|
|
572
|
+
if email:
|
|
573
|
+
conditions.append(f"corporate_email = '{email}'")
|
|
574
|
+
|
|
575
|
+
if not conditions:
|
|
576
|
+
raise ToolError("At least one of employee_id, display_name, or email must be provided.")
|
|
577
|
+
|
|
578
|
+
sql = f"""
|
|
579
|
+
SELECT associate_oid, associate_id, first_name, last_name, display_name, corporate_email as email,
|
|
580
|
+
position_id, job_code, department, department_code
|
|
581
|
+
FROM troc.troc_employees
|
|
582
|
+
WHERE {' AND '.join(conditions)}
|
|
583
|
+
LIMIT 1;
|
|
584
|
+
"""
|
|
585
|
+
try:
|
|
586
|
+
employee_data = await self._get_dataset(
|
|
587
|
+
sql,
|
|
588
|
+
output_format='pandas',
|
|
589
|
+
structured_obj=None
|
|
590
|
+
)
|
|
591
|
+
if employee_data.empty:
|
|
592
|
+
raise ToolError(
|
|
593
|
+
f"No Employee data found for the provided criteria."
|
|
594
|
+
)
|
|
595
|
+
return self._json_encoder(
|
|
596
|
+
employee_data.to_dict(orient='records')
|
|
597
|
+
) # type: ignore[return-value]
|
|
598
|
+
except ToolError as te:
|
|
599
|
+
return f"No Employee data found for the provided criteria, error: {te}"
|
|
600
|
+
except ValueError as ve:
|
|
601
|
+
return f"Invalid data format, error: {ve}"
|
|
602
|
+
except Exception as e:
|
|
603
|
+
return f"Error fetching employee information: {e}"
|
|
604
|
+
|
|
605
|
+
@tool_schema(EmployeeInput)
|
|
606
|
+
async def search_employee(
|
|
607
|
+
self,
|
|
608
|
+
display_name: str = None,
|
|
609
|
+
email: str = None
|
|
610
|
+
) -> str:
|
|
611
|
+
"""Search for employees by their display name or email.
|
|
612
|
+
Returns a list of employees matching the search criteria.
|
|
613
|
+
Useful for finding employees in the system.
|
|
614
|
+
"""
|
|
615
|
+
conditions = []
|
|
616
|
+
if display_name:
|
|
617
|
+
conditions.append(f"display_name ILIKE '%{display_name}%'")
|
|
618
|
+
if email:
|
|
619
|
+
conditions.append(f"corporate_email ILIKE '%{email}%'")
|
|
620
|
+
|
|
621
|
+
if not conditions:
|
|
622
|
+
raise ToolError("At least one of display_name or email must be provided.")
|
|
623
|
+
|
|
624
|
+
sql = f"""
|
|
625
|
+
SELECT associate_oid, associate_id, first_name, last_name, display_name, corporate_email as email,
|
|
626
|
+
position_id, job_code, department, department_code
|
|
627
|
+
FROM troc.troc_employees
|
|
628
|
+
WHERE {' AND '.join(conditions)}
|
|
629
|
+
ORDER BY display_name
|
|
630
|
+
LIMIT 100;
|
|
631
|
+
"""
|
|
632
|
+
try:
|
|
633
|
+
employee_data = await self._get_dataset(
|
|
634
|
+
sql,
|
|
635
|
+
output_format='pandas',
|
|
636
|
+
structured_obj=None
|
|
637
|
+
)
|
|
638
|
+
if employee_data.empty:
|
|
639
|
+
raise ToolError(
|
|
640
|
+
f"No Employee data found for the provided search criteria."
|
|
641
|
+
)
|
|
642
|
+
return self._json_encoder(
|
|
643
|
+
employee_data.to_dict(orient='records')
|
|
644
|
+
) # type: ignore[return-value]
|
|
645
|
+
except ToolError as te:
|
|
646
|
+
return f"No Employee data found for the provided search criteria, error: {te}"
|
|
647
|
+
except ValueError as ve:
|
|
648
|
+
return f"Invalid data format, error: {ve}"
|
|
649
|
+
except Exception as e:
|
|
650
|
+
return f"Error searching for employees: {e}"
|
|
651
|
+
|
|
652
|
+
@tool_schema(EmployeeInput)
|
|
653
|
+
async def get_by_employee_visits(
|
|
654
|
+
self,
|
|
655
|
+
employee_id: str,
|
|
656
|
+
program: str,
|
|
657
|
+
**kwargs
|
|
658
|
+
) -> EmployeeVisit:
|
|
659
|
+
"""Get statistics about visits made by an Employee during the current week.
|
|
660
|
+
Returns detailed visit information for the specified employee.
|
|
661
|
+
Data is returned as a Structured JSON object.
|
|
662
|
+
Useful for analyzing employee visit patterns and performance.
|
|
663
|
+
"""
|
|
664
|
+
if program:
|
|
665
|
+
self.program = program
|
|
666
|
+
if not employee_id:
|
|
667
|
+
employee_id = kwargs.get('email', '').strip().lower()
|
|
668
|
+
sql = await self._get_query("employee_visits")
|
|
669
|
+
sql = sql.format(employee_id=employee_id)
|
|
670
|
+
try:
|
|
671
|
+
visit_data = await self._fetch_one(
|
|
672
|
+
sql,
|
|
673
|
+
output_format='structured',
|
|
674
|
+
structured_obj=EmployeeVisit
|
|
675
|
+
)
|
|
676
|
+
if not visit_data:
|
|
677
|
+
raise ToolError(
|
|
678
|
+
f"No Employee Visit data found for email {employee_id}."
|
|
679
|
+
)
|
|
680
|
+
return visit_data
|
|
681
|
+
except ToolError as te:
|
|
682
|
+
raise ValueError(
|
|
683
|
+
f"No Employee Visit data found for email {employee_id}, error: {te}"
|
|
684
|
+
)
|
|
685
|
+
except ValueError as ve:
|
|
686
|
+
raise ValueError(
|
|
687
|
+
f"Invalid data format, error: {ve}"
|
|
688
|
+
)
|
|
689
|
+
except Exception as e:
|
|
690
|
+
raise ValueError(
|
|
691
|
+
f"Error fetching employee visit data: {e}"
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
@tool_schema(EmployeeInput)
|
|
695
|
+
async def get_employee_sales(
|
|
696
|
+
self,
|
|
697
|
+
employee_id: str,
|
|
698
|
+
program: str,
|
|
699
|
+
**kwargs
|
|
700
|
+
) -> List[EmployeeSales]:
|
|
701
|
+
"""Get sales information for a specific employee.
|
|
702
|
+
Returns a collection of EmployeeSales objects with detailed sales data.
|
|
703
|
+
Useful for analyzing individual employee sales patterns and performance.
|
|
704
|
+
"""
|
|
705
|
+
if program:
|
|
706
|
+
self.program = program
|
|
707
|
+
if not employee_id:
|
|
708
|
+
employee_id = kwargs.get('email', '').strip().lower()
|
|
709
|
+
sql = await self._get_query("employee_sales")
|
|
710
|
+
sql = sql.format(employee_id=employee_id)
|
|
711
|
+
try:
|
|
712
|
+
sales_data = await self._get_dataset(
|
|
713
|
+
sql,
|
|
714
|
+
output_format='structured',
|
|
715
|
+
structured_obj=EmployeeSales
|
|
716
|
+
)
|
|
717
|
+
if not sales_data:
|
|
718
|
+
raise ToolError(
|
|
719
|
+
f"No Employee Sales data found for email {employee_id}."
|
|
720
|
+
)
|
|
721
|
+
return sales_data
|
|
722
|
+
except ToolError as te:
|
|
723
|
+
raise ValueError(
|
|
724
|
+
f"No Employee Sales data found for email {employee_id}, error: {te}"
|
|
725
|
+
)
|
|
726
|
+
except ValueError as ve:
|
|
727
|
+
raise ValueError(
|
|
728
|
+
f"Invalid data format, error: {ve}"
|
|
729
|
+
)
|
|
730
|
+
except Exception as e:
|
|
731
|
+
raise ValueError(
|
|
732
|
+
f"Error fetching employee Sales data: {e}"
|
|
733
|
+
)
|