ai-parrot 0.17.2__cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (535) hide show
  1. agentui/.prettierrc +15 -0
  2. agentui/QUICKSTART.md +272 -0
  3. agentui/README.md +59 -0
  4. agentui/env.example +16 -0
  5. agentui/jsconfig.json +14 -0
  6. agentui/package-lock.json +4242 -0
  7. agentui/package.json +34 -0
  8. agentui/scripts/postinstall/apply-patches.mjs +260 -0
  9. agentui/src/app.css +61 -0
  10. agentui/src/app.d.ts +13 -0
  11. agentui/src/app.html +12 -0
  12. agentui/src/components/LoadingSpinner.svelte +64 -0
  13. agentui/src/components/ThemeSwitcher.svelte +159 -0
  14. agentui/src/components/index.js +4 -0
  15. agentui/src/lib/api/bots.ts +60 -0
  16. agentui/src/lib/api/chat.ts +22 -0
  17. agentui/src/lib/api/http.ts +25 -0
  18. agentui/src/lib/components/BotCard.svelte +33 -0
  19. agentui/src/lib/components/ChatBubble.svelte +63 -0
  20. agentui/src/lib/components/Toast.svelte +21 -0
  21. agentui/src/lib/config.ts +20 -0
  22. agentui/src/lib/stores/auth.svelte.ts +73 -0
  23. agentui/src/lib/stores/theme.svelte.js +64 -0
  24. agentui/src/lib/stores/toast.svelte.ts +31 -0
  25. agentui/src/lib/utils/conversation.ts +39 -0
  26. agentui/src/routes/+layout.svelte +20 -0
  27. agentui/src/routes/+page.svelte +232 -0
  28. agentui/src/routes/login/+page.svelte +200 -0
  29. agentui/src/routes/talk/[agentId]/+page.svelte +297 -0
  30. agentui/src/routes/talk/[agentId]/+page.ts +7 -0
  31. agentui/static/README.md +1 -0
  32. agentui/svelte.config.js +11 -0
  33. agentui/tailwind.config.ts +53 -0
  34. agentui/tsconfig.json +3 -0
  35. agentui/vite.config.ts +10 -0
  36. ai_parrot-0.17.2.dist-info/METADATA +472 -0
  37. ai_parrot-0.17.2.dist-info/RECORD +535 -0
  38. ai_parrot-0.17.2.dist-info/WHEEL +6 -0
  39. ai_parrot-0.17.2.dist-info/entry_points.txt +2 -0
  40. ai_parrot-0.17.2.dist-info/licenses/LICENSE +21 -0
  41. ai_parrot-0.17.2.dist-info/top_level.txt +6 -0
  42. crew-builder/.prettierrc +15 -0
  43. crew-builder/QUICKSTART.md +259 -0
  44. crew-builder/README.md +113 -0
  45. crew-builder/env.example +17 -0
  46. crew-builder/jsconfig.json +14 -0
  47. crew-builder/package-lock.json +4182 -0
  48. crew-builder/package.json +37 -0
  49. crew-builder/scripts/postinstall/apply-patches.mjs +260 -0
  50. crew-builder/src/app.css +62 -0
  51. crew-builder/src/app.d.ts +13 -0
  52. crew-builder/src/app.html +12 -0
  53. crew-builder/src/components/LoadingSpinner.svelte +64 -0
  54. crew-builder/src/components/ThemeSwitcher.svelte +149 -0
  55. crew-builder/src/components/index.js +9 -0
  56. crew-builder/src/lib/api/bots.ts +60 -0
  57. crew-builder/src/lib/api/chat.ts +80 -0
  58. crew-builder/src/lib/api/client.ts +56 -0
  59. crew-builder/src/lib/api/crew/crew.ts +136 -0
  60. crew-builder/src/lib/api/index.ts +5 -0
  61. crew-builder/src/lib/api/o365/auth.ts +65 -0
  62. crew-builder/src/lib/auth/auth.ts +54 -0
  63. crew-builder/src/lib/components/AgentNode.svelte +43 -0
  64. crew-builder/src/lib/components/BotCard.svelte +33 -0
  65. crew-builder/src/lib/components/ChatBubble.svelte +67 -0
  66. crew-builder/src/lib/components/ConfigPanel.svelte +278 -0
  67. crew-builder/src/lib/components/JsonTreeNode.svelte +76 -0
  68. crew-builder/src/lib/components/JsonViewer.svelte +24 -0
  69. crew-builder/src/lib/components/MarkdownEditor.svelte +48 -0
  70. crew-builder/src/lib/components/ThemeToggle.svelte +36 -0
  71. crew-builder/src/lib/components/Toast.svelte +67 -0
  72. crew-builder/src/lib/components/Toolbar.svelte +157 -0
  73. crew-builder/src/lib/components/index.ts +10 -0
  74. crew-builder/src/lib/config.ts +8 -0
  75. crew-builder/src/lib/stores/auth.svelte.ts +228 -0
  76. crew-builder/src/lib/stores/crewStore.ts +369 -0
  77. crew-builder/src/lib/stores/theme.svelte.js +145 -0
  78. crew-builder/src/lib/stores/toast.svelte.ts +69 -0
  79. crew-builder/src/lib/utils/conversation.ts +39 -0
  80. crew-builder/src/lib/utils/markdown.ts +122 -0
  81. crew-builder/src/lib/utils/talkHistory.ts +47 -0
  82. crew-builder/src/routes/+layout.svelte +20 -0
  83. crew-builder/src/routes/+page.svelte +539 -0
  84. crew-builder/src/routes/agents/+page.svelte +247 -0
  85. crew-builder/src/routes/agents/[agentId]/+page.svelte +288 -0
  86. crew-builder/src/routes/agents/[agentId]/+page.ts +7 -0
  87. crew-builder/src/routes/builder/+page.svelte +204 -0
  88. crew-builder/src/routes/crew/ask/+page.svelte +1052 -0
  89. crew-builder/src/routes/crew/ask/+page.ts +1 -0
  90. crew-builder/src/routes/integrations/o365/+page.svelte +304 -0
  91. crew-builder/src/routes/login/+page.svelte +197 -0
  92. crew-builder/src/routes/talk/[agentId]/+page.svelte +487 -0
  93. crew-builder/src/routes/talk/[agentId]/+page.ts +7 -0
  94. crew-builder/static/README.md +1 -0
  95. crew-builder/svelte.config.js +11 -0
  96. crew-builder/tailwind.config.ts +53 -0
  97. crew-builder/tsconfig.json +3 -0
  98. crew-builder/vite.config.ts +10 -0
  99. mcp_servers/calculator_server.py +309 -0
  100. parrot/__init__.py +27 -0
  101. parrot/__pycache__/__init__.cpython-310.pyc +0 -0
  102. parrot/__pycache__/version.cpython-310.pyc +0 -0
  103. parrot/_version.py +34 -0
  104. parrot/a2a/__init__.py +48 -0
  105. parrot/a2a/client.py +658 -0
  106. parrot/a2a/discovery.py +89 -0
  107. parrot/a2a/mixin.py +257 -0
  108. parrot/a2a/models.py +376 -0
  109. parrot/a2a/server.py +770 -0
  110. parrot/agents/__init__.py +29 -0
  111. parrot/bots/__init__.py +12 -0
  112. parrot/bots/a2a_agent.py +19 -0
  113. parrot/bots/abstract.py +3139 -0
  114. parrot/bots/agent.py +1129 -0
  115. parrot/bots/basic.py +9 -0
  116. parrot/bots/chatbot.py +669 -0
  117. parrot/bots/data.py +1618 -0
  118. parrot/bots/database/__init__.py +5 -0
  119. parrot/bots/database/abstract.py +3071 -0
  120. parrot/bots/database/cache.py +286 -0
  121. parrot/bots/database/models.py +468 -0
  122. parrot/bots/database/prompts.py +154 -0
  123. parrot/bots/database/retries.py +98 -0
  124. parrot/bots/database/router.py +269 -0
  125. parrot/bots/database/sql.py +41 -0
  126. parrot/bots/db/__init__.py +6 -0
  127. parrot/bots/db/abstract.py +556 -0
  128. parrot/bots/db/bigquery.py +602 -0
  129. parrot/bots/db/cache.py +85 -0
  130. parrot/bots/db/documentdb.py +668 -0
  131. parrot/bots/db/elastic.py +1014 -0
  132. parrot/bots/db/influx.py +898 -0
  133. parrot/bots/db/mock.py +96 -0
  134. parrot/bots/db/multi.py +783 -0
  135. parrot/bots/db/prompts.py +185 -0
  136. parrot/bots/db/sql.py +1255 -0
  137. parrot/bots/db/tools.py +212 -0
  138. parrot/bots/document.py +680 -0
  139. parrot/bots/hrbot.py +15 -0
  140. parrot/bots/kb.py +170 -0
  141. parrot/bots/mcp.py +36 -0
  142. parrot/bots/orchestration/README.md +463 -0
  143. parrot/bots/orchestration/__init__.py +1 -0
  144. parrot/bots/orchestration/agent.py +155 -0
  145. parrot/bots/orchestration/crew.py +3330 -0
  146. parrot/bots/orchestration/fsm.py +1179 -0
  147. parrot/bots/orchestration/hr.py +434 -0
  148. parrot/bots/orchestration/storage/__init__.py +4 -0
  149. parrot/bots/orchestration/storage/memory.py +100 -0
  150. parrot/bots/orchestration/storage/mixin.py +119 -0
  151. parrot/bots/orchestration/verify.py +202 -0
  152. parrot/bots/product.py +204 -0
  153. parrot/bots/prompts/__init__.py +96 -0
  154. parrot/bots/prompts/agents.py +155 -0
  155. parrot/bots/prompts/data.py +216 -0
  156. parrot/bots/prompts/output_generation.py +8 -0
  157. parrot/bots/scraper/__init__.py +3 -0
  158. parrot/bots/scraper/models.py +122 -0
  159. parrot/bots/scraper/scraper.py +1173 -0
  160. parrot/bots/scraper/templates.py +115 -0
  161. parrot/bots/stores/__init__.py +5 -0
  162. parrot/bots/stores/local.py +172 -0
  163. parrot/bots/webdev.py +81 -0
  164. parrot/cli.py +17 -0
  165. parrot/clients/__init__.py +16 -0
  166. parrot/clients/base.py +1491 -0
  167. parrot/clients/claude.py +1191 -0
  168. parrot/clients/factory.py +129 -0
  169. parrot/clients/google.py +4567 -0
  170. parrot/clients/gpt.py +1975 -0
  171. parrot/clients/grok.py +432 -0
  172. parrot/clients/groq.py +986 -0
  173. parrot/clients/hf.py +582 -0
  174. parrot/clients/models.py +18 -0
  175. parrot/conf.py +395 -0
  176. parrot/embeddings/__init__.py +9 -0
  177. parrot/embeddings/base.py +157 -0
  178. parrot/embeddings/google.py +98 -0
  179. parrot/embeddings/huggingface.py +74 -0
  180. parrot/embeddings/openai.py +84 -0
  181. parrot/embeddings/processor.py +88 -0
  182. parrot/exceptions.c +13868 -0
  183. parrot/exceptions.cpython-310-x86_64-linux-gnu.so +0 -0
  184. parrot/exceptions.pxd +22 -0
  185. parrot/exceptions.pxi +15 -0
  186. parrot/exceptions.pyx +44 -0
  187. parrot/generators/__init__.py +29 -0
  188. parrot/generators/base.py +200 -0
  189. parrot/generators/html.py +293 -0
  190. parrot/generators/react.py +205 -0
  191. parrot/generators/streamlit.py +203 -0
  192. parrot/generators/template.py +105 -0
  193. parrot/handlers/__init__.py +4 -0
  194. parrot/handlers/agent.py +861 -0
  195. parrot/handlers/agents/__init__.py +1 -0
  196. parrot/handlers/agents/abstract.py +900 -0
  197. parrot/handlers/bots.py +338 -0
  198. parrot/handlers/chat.py +915 -0
  199. parrot/handlers/creation.sql +192 -0
  200. parrot/handlers/crew/ARCHITECTURE.md +362 -0
  201. parrot/handlers/crew/README_BOTMANAGER_PERSISTENCE.md +303 -0
  202. parrot/handlers/crew/README_REDIS_PERSISTENCE.md +366 -0
  203. parrot/handlers/crew/__init__.py +0 -0
  204. parrot/handlers/crew/handler.py +801 -0
  205. parrot/handlers/crew/models.py +229 -0
  206. parrot/handlers/crew/redis_persistence.py +523 -0
  207. parrot/handlers/jobs/__init__.py +10 -0
  208. parrot/handlers/jobs/job.py +384 -0
  209. parrot/handlers/jobs/mixin.py +627 -0
  210. parrot/handlers/jobs/models.py +115 -0
  211. parrot/handlers/jobs/worker.py +31 -0
  212. parrot/handlers/models.py +596 -0
  213. parrot/handlers/o365_auth.py +105 -0
  214. parrot/handlers/stream.py +337 -0
  215. parrot/interfaces/__init__.py +6 -0
  216. parrot/interfaces/aws.py +143 -0
  217. parrot/interfaces/credentials.py +113 -0
  218. parrot/interfaces/database.py +27 -0
  219. parrot/interfaces/google.py +1123 -0
  220. parrot/interfaces/hierarchy.py +1227 -0
  221. parrot/interfaces/http.py +651 -0
  222. parrot/interfaces/images/__init__.py +0 -0
  223. parrot/interfaces/images/plugins/__init__.py +24 -0
  224. parrot/interfaces/images/plugins/abstract.py +58 -0
  225. parrot/interfaces/images/plugins/analisys.py +148 -0
  226. parrot/interfaces/images/plugins/classify.py +150 -0
  227. parrot/interfaces/images/plugins/classifybase.py +182 -0
  228. parrot/interfaces/images/plugins/detect.py +150 -0
  229. parrot/interfaces/images/plugins/exif.py +1103 -0
  230. parrot/interfaces/images/plugins/hash.py +52 -0
  231. parrot/interfaces/images/plugins/vision.py +104 -0
  232. parrot/interfaces/images/plugins/yolo.py +66 -0
  233. parrot/interfaces/images/plugins/zerodetect.py +197 -0
  234. parrot/interfaces/o365.py +978 -0
  235. parrot/interfaces/onedrive.py +822 -0
  236. parrot/interfaces/sharepoint.py +1435 -0
  237. parrot/interfaces/soap.py +257 -0
  238. parrot/loaders/__init__.py +8 -0
  239. parrot/loaders/abstract.py +1131 -0
  240. parrot/loaders/audio.py +199 -0
  241. parrot/loaders/basepdf.py +53 -0
  242. parrot/loaders/basevideo.py +1568 -0
  243. parrot/loaders/csv.py +409 -0
  244. parrot/loaders/docx.py +116 -0
  245. parrot/loaders/epubloader.py +316 -0
  246. parrot/loaders/excel.py +199 -0
  247. parrot/loaders/factory.py +55 -0
  248. parrot/loaders/files/__init__.py +0 -0
  249. parrot/loaders/files/abstract.py +39 -0
  250. parrot/loaders/files/html.py +26 -0
  251. parrot/loaders/files/text.py +63 -0
  252. parrot/loaders/html.py +152 -0
  253. parrot/loaders/markdown.py +442 -0
  254. parrot/loaders/pdf.py +373 -0
  255. parrot/loaders/pdfmark.py +320 -0
  256. parrot/loaders/pdftables.py +506 -0
  257. parrot/loaders/ppt.py +476 -0
  258. parrot/loaders/qa.py +63 -0
  259. parrot/loaders/splitters/__init__.py +10 -0
  260. parrot/loaders/splitters/base.py +138 -0
  261. parrot/loaders/splitters/md.py +228 -0
  262. parrot/loaders/splitters/token.py +143 -0
  263. parrot/loaders/txt.py +26 -0
  264. parrot/loaders/video.py +89 -0
  265. parrot/loaders/videolocal.py +218 -0
  266. parrot/loaders/videounderstanding.py +377 -0
  267. parrot/loaders/vimeo.py +167 -0
  268. parrot/loaders/web.py +599 -0
  269. parrot/loaders/youtube.py +504 -0
  270. parrot/manager/__init__.py +5 -0
  271. parrot/manager/manager.py +1030 -0
  272. parrot/mcp/__init__.py +28 -0
  273. parrot/mcp/adapter.py +105 -0
  274. parrot/mcp/cli.py +174 -0
  275. parrot/mcp/client.py +119 -0
  276. parrot/mcp/config.py +75 -0
  277. parrot/mcp/integration.py +842 -0
  278. parrot/mcp/oauth.py +933 -0
  279. parrot/mcp/server.py +225 -0
  280. parrot/mcp/transports/__init__.py +3 -0
  281. parrot/mcp/transports/base.py +279 -0
  282. parrot/mcp/transports/grpc_session.py +163 -0
  283. parrot/mcp/transports/http.py +312 -0
  284. parrot/mcp/transports/mcp.proto +108 -0
  285. parrot/mcp/transports/quic.py +1082 -0
  286. parrot/mcp/transports/sse.py +330 -0
  287. parrot/mcp/transports/stdio.py +309 -0
  288. parrot/mcp/transports/unix.py +395 -0
  289. parrot/mcp/transports/websocket.py +547 -0
  290. parrot/memory/__init__.py +16 -0
  291. parrot/memory/abstract.py +209 -0
  292. parrot/memory/agent.py +32 -0
  293. parrot/memory/cache.py +175 -0
  294. parrot/memory/core.py +555 -0
  295. parrot/memory/file.py +153 -0
  296. parrot/memory/mem.py +131 -0
  297. parrot/memory/redis.py +613 -0
  298. parrot/models/__init__.py +46 -0
  299. parrot/models/basic.py +118 -0
  300. parrot/models/compliance.py +208 -0
  301. parrot/models/crew.py +395 -0
  302. parrot/models/detections.py +654 -0
  303. parrot/models/generation.py +85 -0
  304. parrot/models/google.py +223 -0
  305. parrot/models/groq.py +23 -0
  306. parrot/models/openai.py +30 -0
  307. parrot/models/outputs.py +285 -0
  308. parrot/models/responses.py +938 -0
  309. parrot/notifications/__init__.py +743 -0
  310. parrot/openapi/__init__.py +3 -0
  311. parrot/openapi/components.yaml +641 -0
  312. parrot/openapi/config.py +322 -0
  313. parrot/outputs/__init__.py +32 -0
  314. parrot/outputs/formats/__init__.py +108 -0
  315. parrot/outputs/formats/altair.py +359 -0
  316. parrot/outputs/formats/application.py +122 -0
  317. parrot/outputs/formats/base.py +351 -0
  318. parrot/outputs/formats/bokeh.py +356 -0
  319. parrot/outputs/formats/card.py +424 -0
  320. parrot/outputs/formats/chart.py +436 -0
  321. parrot/outputs/formats/d3.py +255 -0
  322. parrot/outputs/formats/echarts.py +310 -0
  323. parrot/outputs/formats/generators/__init__.py +0 -0
  324. parrot/outputs/formats/generators/abstract.py +61 -0
  325. parrot/outputs/formats/generators/panel.py +145 -0
  326. parrot/outputs/formats/generators/streamlit.py +86 -0
  327. parrot/outputs/formats/generators/terminal.py +63 -0
  328. parrot/outputs/formats/holoviews.py +310 -0
  329. parrot/outputs/formats/html.py +147 -0
  330. parrot/outputs/formats/jinja2.py +46 -0
  331. parrot/outputs/formats/json.py +87 -0
  332. parrot/outputs/formats/map.py +933 -0
  333. parrot/outputs/formats/markdown.py +172 -0
  334. parrot/outputs/formats/matplotlib.py +237 -0
  335. parrot/outputs/formats/mixins/__init__.py +0 -0
  336. parrot/outputs/formats/mixins/emaps.py +855 -0
  337. parrot/outputs/formats/plotly.py +341 -0
  338. parrot/outputs/formats/seaborn.py +310 -0
  339. parrot/outputs/formats/table.py +397 -0
  340. parrot/outputs/formats/template_report.py +138 -0
  341. parrot/outputs/formats/yaml.py +125 -0
  342. parrot/outputs/formatter.py +152 -0
  343. parrot/outputs/templates/__init__.py +95 -0
  344. parrot/pipelines/__init__.py +0 -0
  345. parrot/pipelines/abstract.py +210 -0
  346. parrot/pipelines/detector.py +124 -0
  347. parrot/pipelines/models.py +90 -0
  348. parrot/pipelines/planogram.py +3002 -0
  349. parrot/pipelines/table.sql +97 -0
  350. parrot/plugins/__init__.py +106 -0
  351. parrot/plugins/importer.py +80 -0
  352. parrot/py.typed +0 -0
  353. parrot/registry/__init__.py +18 -0
  354. parrot/registry/registry.py +594 -0
  355. parrot/scheduler/__init__.py +1189 -0
  356. parrot/scheduler/models.py +60 -0
  357. parrot/security/__init__.py +16 -0
  358. parrot/security/prompt_injection.py +268 -0
  359. parrot/security/security_events.sql +25 -0
  360. parrot/services/__init__.py +1 -0
  361. parrot/services/mcp/__init__.py +8 -0
  362. parrot/services/mcp/config.py +13 -0
  363. parrot/services/mcp/server.py +295 -0
  364. parrot/services/o365_remote_auth.py +235 -0
  365. parrot/stores/__init__.py +7 -0
  366. parrot/stores/abstract.py +352 -0
  367. parrot/stores/arango.py +1090 -0
  368. parrot/stores/bigquery.py +1377 -0
  369. parrot/stores/cache.py +106 -0
  370. parrot/stores/empty.py +10 -0
  371. parrot/stores/faiss_store.py +1157 -0
  372. parrot/stores/kb/__init__.py +9 -0
  373. parrot/stores/kb/abstract.py +68 -0
  374. parrot/stores/kb/cache.py +165 -0
  375. parrot/stores/kb/doc.py +325 -0
  376. parrot/stores/kb/hierarchy.py +346 -0
  377. parrot/stores/kb/local.py +457 -0
  378. parrot/stores/kb/prompt.py +28 -0
  379. parrot/stores/kb/redis.py +659 -0
  380. parrot/stores/kb/store.py +115 -0
  381. parrot/stores/kb/user.py +374 -0
  382. parrot/stores/models.py +59 -0
  383. parrot/stores/pgvector.py +3 -0
  384. parrot/stores/postgres.py +2853 -0
  385. parrot/stores/utils/__init__.py +0 -0
  386. parrot/stores/utils/chunking.py +197 -0
  387. parrot/telemetry/__init__.py +3 -0
  388. parrot/telemetry/mixin.py +111 -0
  389. parrot/template/__init__.py +3 -0
  390. parrot/template/engine.py +259 -0
  391. parrot/tools/__init__.py +23 -0
  392. parrot/tools/abstract.py +644 -0
  393. parrot/tools/agent.py +363 -0
  394. parrot/tools/arangodbsearch.py +537 -0
  395. parrot/tools/arxiv_tool.py +188 -0
  396. parrot/tools/calculator/__init__.py +3 -0
  397. parrot/tools/calculator/operations/__init__.py +38 -0
  398. parrot/tools/calculator/operations/calculus.py +80 -0
  399. parrot/tools/calculator/operations/statistics.py +76 -0
  400. parrot/tools/calculator/tool.py +150 -0
  401. parrot/tools/cloudwatch.py +988 -0
  402. parrot/tools/codeinterpreter/__init__.py +127 -0
  403. parrot/tools/codeinterpreter/executor.py +371 -0
  404. parrot/tools/codeinterpreter/internals.py +473 -0
  405. parrot/tools/codeinterpreter/models.py +643 -0
  406. parrot/tools/codeinterpreter/prompts.py +224 -0
  407. parrot/tools/codeinterpreter/tool.py +664 -0
  408. parrot/tools/company_info/__init__.py +6 -0
  409. parrot/tools/company_info/tool.py +1138 -0
  410. parrot/tools/correlationanalysis.py +437 -0
  411. parrot/tools/database/abstract.py +286 -0
  412. parrot/tools/database/bq.py +115 -0
  413. parrot/tools/database/cache.py +284 -0
  414. parrot/tools/database/models.py +95 -0
  415. parrot/tools/database/pg.py +343 -0
  416. parrot/tools/databasequery.py +1159 -0
  417. parrot/tools/db.py +1800 -0
  418. parrot/tools/ddgo.py +370 -0
  419. parrot/tools/decorators.py +271 -0
  420. parrot/tools/dftohtml.py +282 -0
  421. parrot/tools/document.py +549 -0
  422. parrot/tools/ecs.py +819 -0
  423. parrot/tools/edareport.py +368 -0
  424. parrot/tools/elasticsearch.py +1049 -0
  425. parrot/tools/employees.py +462 -0
  426. parrot/tools/epson/__init__.py +96 -0
  427. parrot/tools/excel.py +683 -0
  428. parrot/tools/file/__init__.py +13 -0
  429. parrot/tools/file/abstract.py +76 -0
  430. parrot/tools/file/gcs.py +378 -0
  431. parrot/tools/file/local.py +284 -0
  432. parrot/tools/file/s3.py +511 -0
  433. parrot/tools/file/tmp.py +309 -0
  434. parrot/tools/file/tool.py +501 -0
  435. parrot/tools/file_reader.py +129 -0
  436. parrot/tools/flowtask/__init__.py +19 -0
  437. parrot/tools/flowtask/tool.py +761 -0
  438. parrot/tools/gittoolkit.py +508 -0
  439. parrot/tools/google/__init__.py +18 -0
  440. parrot/tools/google/base.py +169 -0
  441. parrot/tools/google/tools.py +1251 -0
  442. parrot/tools/googlelocation.py +5 -0
  443. parrot/tools/googleroutes.py +5 -0
  444. parrot/tools/googlesearch.py +5 -0
  445. parrot/tools/googlesitesearch.py +5 -0
  446. parrot/tools/googlevoice.py +2 -0
  447. parrot/tools/gvoice.py +695 -0
  448. parrot/tools/ibisworld/README.md +225 -0
  449. parrot/tools/ibisworld/__init__.py +11 -0
  450. parrot/tools/ibisworld/tool.py +366 -0
  451. parrot/tools/jiratoolkit.py +1718 -0
  452. parrot/tools/manager.py +1098 -0
  453. parrot/tools/math.py +152 -0
  454. parrot/tools/metadata.py +476 -0
  455. parrot/tools/msteams.py +1621 -0
  456. parrot/tools/msword.py +635 -0
  457. parrot/tools/multidb.py +580 -0
  458. parrot/tools/multistoresearch.py +369 -0
  459. parrot/tools/networkninja.py +167 -0
  460. parrot/tools/nextstop/__init__.py +4 -0
  461. parrot/tools/nextstop/base.py +286 -0
  462. parrot/tools/nextstop/employee.py +733 -0
  463. parrot/tools/nextstop/store.py +462 -0
  464. parrot/tools/notification.py +435 -0
  465. parrot/tools/o365/__init__.py +42 -0
  466. parrot/tools/o365/base.py +295 -0
  467. parrot/tools/o365/bundle.py +522 -0
  468. parrot/tools/o365/events.py +554 -0
  469. parrot/tools/o365/mail.py +992 -0
  470. parrot/tools/o365/onedrive.py +497 -0
  471. parrot/tools/o365/sharepoint.py +641 -0
  472. parrot/tools/openapi_toolkit.py +904 -0
  473. parrot/tools/openweather.py +527 -0
  474. parrot/tools/pdfprint.py +1001 -0
  475. parrot/tools/powerbi.py +518 -0
  476. parrot/tools/powerpoint.py +1113 -0
  477. parrot/tools/pricestool.py +146 -0
  478. parrot/tools/products/__init__.py +246 -0
  479. parrot/tools/prophet_tool.py +171 -0
  480. parrot/tools/pythonpandas.py +630 -0
  481. parrot/tools/pythonrepl.py +910 -0
  482. parrot/tools/qsource.py +436 -0
  483. parrot/tools/querytoolkit.py +395 -0
  484. parrot/tools/quickeda.py +827 -0
  485. parrot/tools/resttool.py +553 -0
  486. parrot/tools/retail/__init__.py +0 -0
  487. parrot/tools/retail/bby.py +528 -0
  488. parrot/tools/sandboxtool.py +703 -0
  489. parrot/tools/sassie/__init__.py +352 -0
  490. parrot/tools/scraping/__init__.py +7 -0
  491. parrot/tools/scraping/docs/select.md +466 -0
  492. parrot/tools/scraping/documentation.md +1278 -0
  493. parrot/tools/scraping/driver.py +436 -0
  494. parrot/tools/scraping/models.py +576 -0
  495. parrot/tools/scraping/options.py +85 -0
  496. parrot/tools/scraping/orchestrator.py +517 -0
  497. parrot/tools/scraping/readme.md +740 -0
  498. parrot/tools/scraping/tool.py +3115 -0
  499. parrot/tools/seasonaldetection.py +642 -0
  500. parrot/tools/shell_tool/__init__.py +5 -0
  501. parrot/tools/shell_tool/actions.py +408 -0
  502. parrot/tools/shell_tool/engine.py +155 -0
  503. parrot/tools/shell_tool/models.py +322 -0
  504. parrot/tools/shell_tool/tool.py +442 -0
  505. parrot/tools/site_search.py +214 -0
  506. parrot/tools/textfile.py +418 -0
  507. parrot/tools/think.py +378 -0
  508. parrot/tools/toolkit.py +298 -0
  509. parrot/tools/webapp_tool.py +187 -0
  510. parrot/tools/whatif.py +1279 -0
  511. parrot/tools/workday/MULTI_WSDL_EXAMPLE.md +249 -0
  512. parrot/tools/workday/__init__.py +6 -0
  513. parrot/tools/workday/models.py +1389 -0
  514. parrot/tools/workday/tool.py +1293 -0
  515. parrot/tools/yfinance_tool.py +306 -0
  516. parrot/tools/zipcode.py +217 -0
  517. parrot/utils/__init__.py +2 -0
  518. parrot/utils/helpers.py +73 -0
  519. parrot/utils/parsers/__init__.py +5 -0
  520. parrot/utils/parsers/toml.c +12078 -0
  521. parrot/utils/parsers/toml.cpython-310-x86_64-linux-gnu.so +0 -0
  522. parrot/utils/parsers/toml.pyx +21 -0
  523. parrot/utils/toml.py +11 -0
  524. parrot/utils/types.cpp +20936 -0
  525. parrot/utils/types.cpython-310-x86_64-linux-gnu.so +0 -0
  526. parrot/utils/types.pyx +213 -0
  527. parrot/utils/uv.py +11 -0
  528. parrot/version.py +10 -0
  529. parrot/yaml-rs/Cargo.lock +350 -0
  530. parrot/yaml-rs/Cargo.toml +19 -0
  531. parrot/yaml-rs/pyproject.toml +19 -0
  532. parrot/yaml-rs/python/yaml_rs/__init__.py +81 -0
  533. parrot/yaml-rs/src/lib.rs +222 -0
  534. requirements/docker-compose.yml +24 -0
  535. requirements/requirements-dev.txt +21 -0
@@ -0,0 +1,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
+ )