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,654 @@
1
+ from typing import List, Dict, Optional, Literal, Any, Mapping
2
+ import uuid
3
+ from pydantic import BaseModel, Field, field_validator
4
+
5
+ class BoundingBox(BaseModel):
6
+ """Normalized bounding box coordinates"""
7
+ x1: float = Field(..., description="The leftmost x-coordinate (normalized)", ge=0, le=1)
8
+ y1: float = Field(..., description="The topmost y-coordinate (normalized)", ge=0, le=1)
9
+ x2: float = Field(..., description="The rightmost x-coordinate (normalized)", ge=0, le=1)
10
+ y2: float = Field(..., description="The bottommost y-coordinate (normalized)", ge=0, le=1)
11
+
12
+ def get_coordinates(self) -> tuple[float, float, float, float]:
13
+ """Return bounding box as (x1, y1, x2, y2)"""
14
+ return (self.x1, self.y1, self.x2, self.y2)
15
+
16
+ def get_pixel_coordinates(self, width: int, height: int) -> tuple[int, int, int, int]:
17
+ """Return bounding box as (x1, y1, x2, y2) absolute integer pixels."""
18
+ px1 = int(self.x1 * width)
19
+ py1 = int(self.y1 * height)
20
+ px2 = int(self.x2 * width)
21
+ py2 = int(self.y2 * height)
22
+ return (px1, py1, px2, py2)
23
+
24
+ class Detection(BaseModel):
25
+ """Generic detection result"""
26
+ label: Optional[str] = Field(None, description="Optional label for the detection")
27
+ confidence: float = Field(ge=0.0, le=1.0, description="Detection confidence")
28
+ content: Optional[str] = Field(None, description="The recognized text content within the bounding box, if any.")
29
+ bbox: BoundingBox
30
+
31
+
32
+ class Detections(BaseModel):
33
+ """Collection of detections in an image"""
34
+ detections: List[Detection] = Field(default_factory=list, description="List of detected bounding boxes")
35
+
36
+
37
+ class DetectionBox(BaseModel):
38
+ """Bounding box from object detection"""
39
+ x1: int = Field(description="Left x coordinate")
40
+ y1: int = Field(description="Top y coordinate")
41
+ x2: int = Field(description="Right x coordinate")
42
+ y2: int = Field(description="Bottom y coordinate")
43
+ confidence: float = Field(ge=0.0, le=1.0, description="Detection confidence")
44
+ class_id: int = Field(default=None, description="Detected class ID")
45
+ class_name: str = Field(default=None, description="Detected class name")
46
+ area: int = Field(default=None, description="Bounding box area in pixels")
47
+ label: Optional[str] = Field(None, description="Optional label for the detection")
48
+ ocr_text: Optional[str] = Field(
49
+ None,
50
+ description="OCR text within the bounding box, if any"
51
+ )
52
+
53
+ class ShelfRegion(BaseModel):
54
+ """Detected shelf region"""
55
+ shelf_id: str = Field(description="Unique shelf identifier")
56
+ bbox: DetectionBox = Field(description="Shelf bounding box")
57
+ level: str = Field(description="Shelf level (top, middle, bottom)")
58
+ objects: List[DetectionBox] = Field(default_factory=list, description="Objects on this shelf")
59
+
60
+
61
+ class IdentifiedProduct(BaseModel):
62
+ """Product identified by LLM using reference images"""
63
+ detection_id: int = Field(None, description="The unique ID of the corresponding detection box.")
64
+ product_type: str = Field(description="Type of product")
65
+ product_model: Optional[str] = Field(None, description="Specific product model")
66
+ brand: Optional[str] = Field(None, description="Brand on the item (e.g., Epson)")
67
+ confidence: float = Field(ge=0.0, le=1.0, description="Confidence score")
68
+ visual_features: List[str] = Field(default_factory=list, description="List of key visual identifiers.")
69
+ reference_match: Optional[str] = Field(None, description="Which reference image was matched, or 'none'.")
70
+ shelf_location: Optional[str] = Field(
71
+ None, description="The shelf level where the product is located: 'header', 'top', 'middle', 'bottom'."
72
+ )
73
+ position_on_shelf: Optional[str] = Field(
74
+ None, description="Position on the shelf: 'left', 'center', 'right'."
75
+ )
76
+ advertisement_type: Optional[str] = Field(
77
+ None, description="Ad type if promotional (backlit_graphic, endcap_poster, shelf_talker, banner, digital_display)"
78
+ )
79
+ detection_box: Optional[DetectionBox] = Field(None, description="Detection box information")
80
+ extra: Dict[str, str] = Field(default_factory=dict, description="Any Extra descriptive tags")
81
+
82
+ @field_validator('confidence', mode='before')
83
+ @classmethod
84
+ def validate_confidence(cls, v: Any) -> float:
85
+ """Ensure confidence is between 0 and 1."""
86
+ if isinstance(v, str):
87
+ if v.lower() == 'high':
88
+ return 0.9
89
+ elif v.lower() == 'medium':
90
+ return 0.6
91
+ elif v.lower() == 'low':
92
+ return 0.3
93
+ else:
94
+ return 0.5 # Default for unrecognized strings
95
+ if not (0 <= v <= 1):
96
+ raise ValueError(f"confidence must be between 0 and 1, got {v}")
97
+ return v
98
+
99
+ @field_validator('position_on_shelf', mode='before')
100
+ @classmethod
101
+ def validate_position_on_shelf(cls, v: Any) -> Optional[str]:
102
+ """Ensure position_on_shelf is one of the accepted values."""
103
+ if isinstance(v, int):
104
+ mapping = {0: "left", 1: "center", 2: "right"}
105
+ return mapping.get(v, None)
106
+ valid_positions = {"left", "center", "right"}
107
+ if v is not None and v.lower() not in valid_positions:
108
+ raise ValueError(f"position_on_shelf must be one of {valid_positions}, got '{v}'")
109
+ return v.lower() if v else v
110
+
111
+ @field_validator('detection_id', mode='before')
112
+ @classmethod
113
+ def set_id_for_llm_found_items(cls, v: Any) -> int:
114
+ """If detection_id is null, generate a unique negative ID."""
115
+ if v is None:
116
+ # Generate a unique integer to avoid collisions. Negative values clearly
117
+ # indicate that this item was found by the LLM, not YOLO.
118
+ return -int(str(uuid.uuid4().int)[:8])
119
+ return v
120
+
121
+ # VALIDATOR 2: Converts a coordinate list into a DetectionBox object.
122
+ @field_validator('detection_box', mode='before')
123
+ @classmethod
124
+ def convert_list_to_detection_box(cls, v: Any, values: Any) -> Any:
125
+ """If detection_box is a list [x1,y1,x2,y2], convert it to a DetectionBox object."""
126
+ # The 'v' is the value of the 'detection_box' field itself.
127
+ if isinstance(v, list) and len(v) == 4:
128
+ x1, y1, x2, y2 = v
129
+
130
+ # We need the confidence to create a valid DetectionBox.
131
+ # 'values.data' gives us access to the other raw data in the JSON object.
132
+ confidence = values.data.get('confidence', 0.95) # Default to 0.95 if not found
133
+
134
+ return DetectionBox(
135
+ x1=int(x1),
136
+ y1=int(y1),
137
+ x2=int(x2),
138
+ y2=int(y2),
139
+ confidence=float(confidence),
140
+ class_id=0, # Placeholder ID for LLM-found items
141
+ class_name='llm_detected',
142
+ area=abs(int(x2) - int(x1)) * abs(int(y2) - int(y1))
143
+ )
144
+ # If it's already a dict or a DetectionBox object, pass it through.
145
+ return v
146
+
147
+ class IdentificationResponse(BaseModel):
148
+ """Response from product identification"""
149
+ identified_products: List[IdentifiedProduct] = Field(
150
+ alias="detections",
151
+ description="List of identified products from the image"
152
+ )
153
+
154
+ # Enhanced models for pipeline planogram description
155
+ class BrandDetectionConfig(BaseModel):
156
+ """Configuration for brand detection parameters"""
157
+ enabled: bool = Field(default=True, description="Enable brand detection")
158
+ target_brands: List[str] = Field(default_factory=list, description="List of brands to detect")
159
+ confidence_threshold: float = Field(default=0.7, description="Minimum confidence for brand detection")
160
+ ocr_enabled: bool = Field(default=True, description="Use OCR for brand text detection")
161
+ case_sensitive: bool = Field(default=False, description="Case-sensitive brand matching")
162
+
163
+ class CategoryDetectionConfig(BaseModel):
164
+ """Configuration for product category detection"""
165
+ enabled: bool = Field(default=True, description="Enable category detection")
166
+ target_categories: List[str] = Field(default_factory=list, description="Categories to detect (printers, boxes, etc.)")
167
+ size_based_classification: bool = Field(default=True, description="Use size for category classification")
168
+ visual_features_weight: float = Field(default=0.6, description="Weight of visual features in classification")
169
+ confidence_threshold: float = Field(default=0.6, description="Minimum confidence for category detection")
170
+
171
+ class ShelfProduct(BaseModel):
172
+ """Configuration for products expected on a shelf"""
173
+ name: str = Field(description="Product name/model")
174
+ product_type: str = Field(description="Type: printer, product_box, promotional_graphic, etc.")
175
+ quantity_range: tuple[int, int] = Field(default=(1, 1), description="Min and max quantity expected")
176
+ position_preference: Optional[Literal["left", "center", "right"]] = Field(default=None, description="Preferred position on shelf")
177
+ mandatory: bool = Field(default=True, description="Whether this product is required")
178
+ visual_features: Optional[List[str]] = Field(default=None, description="Expected key visual identifiers and features for this product")
179
+
180
+ class ShelfConfig(BaseModel):
181
+ """Configuration for a single shelf"""
182
+ level: str = Field(description="Shelf level: header, top, middle, bottom")
183
+ products: List[ShelfProduct] = Field(description="Expected products on this shelf")
184
+ compliance_threshold: float = Field(default=0.8, description="Compliance threshold for this shelf")
185
+ allow_extra_products: bool = Field(default=False, description="Allow products not in the specification")
186
+ position_strict: bool = Field(default=False, description="Enforce strict positioning")
187
+ height_ratio: Optional[float] = Field(default=0.30) # Add this field: 0.30 = 30% of ROI height
188
+
189
+ class TextRequirement(BaseModel):
190
+ """Text requirement for promotional materials"""
191
+ required_text: str = Field(description="Text that must be present")
192
+ match_type: Literal["exact", "contains", "regex"] = Field(default="contains", description="Type of text matching")
193
+ case_sensitive: bool = Field(default=False, description="Case-sensitive matching")
194
+ confidence_threshold: float = Field(default=0.7, description="OCR confidence threshold")
195
+ mandatory: bool = Field(default=True, description="Whether this text is required")
196
+
197
+
198
+ class AdvertisementEndcap(BaseModel):
199
+ """Configuration for advertisement endcap"""
200
+ enabled: bool = Field(default=True, description="Whether endcap advertisement is present")
201
+ promotional_type: Literal["backlit_graphic", "endcap_poster", "shelf_talker", "banner", "digital_display", "integrated_display", "promotional_base"] = Field(
202
+ default="backlit_graphic", description="Type of promotional display"
203
+ )
204
+ position: Literal["header", "top", "middle", "bottom", "side"] = Field(default="header", description="Position of endcap")
205
+ product_weight: float = Field(default=0.8, description="Weight of product compliance in overall score")
206
+ text_weight: float = Field(default=0.2, description="Weight of text compliance in overall score")
207
+ top_margin_percent: float = Field(default=0.02, description="Top margin percent of image for panel detection")
208
+ width_margin_percent: float = Field(default=0.45, description="Width percent of image for panel detection")
209
+ height_margin_percent: float = Field(default=0.33, description="Height percent of image for panel detection")
210
+ side_margin_percent: float = Field(default=0.05, description="Side margin percent for panel detection")
211
+ brand_requirements: List[str] = Field(default_factory=list, description="Required brand elements")
212
+ text_requirements: List[TextRequirement] = Field(default_factory=list, description="Required text elements")
213
+ reference_image_path: Optional[str] = Field(default=None, description="Path to reference image for comparison")
214
+ allow_additional_text: bool = Field(default=True, description="Allow additional text beyond requirements")
215
+
216
+ class AisleConfig(BaseModel):
217
+ """Configuration for aisle-specific settings"""
218
+ name: str = Field(description="Aisle name (electronics, furniture, etc.)")
219
+ category_hints: List[str] = Field(default_factory=list, description="Product category hints for this aisle")
220
+ lighting_conditions: Literal["bright", "normal", "dim", "retail_standard"] = Field(default="normal", description="Expected lighting")
221
+ shelf_spacing: Optional[float] = Field(default=None, description="Expected spacing between shelves")
222
+
223
+
224
+ class PlanogramDescription(BaseModel):
225
+ """
226
+ Comprehensive, configurable planogram description
227
+ """
228
+ # Basic identification
229
+ brand: str = Field(description="Primary brand for this planogram")
230
+ category: str = Field(description="Product category")
231
+ aisle: AisleConfig = Field(description="Aisle configuration")
232
+ tags: List[str] = Field(default_factory=list, description="Tags for special features or promotions")
233
+ advertisement: Dict[str, Any] = Field(default_factory=dict, description="Advertisement sizing and positioning")
234
+ text_tokens: List[str] = Field(default_factory=list, description="Additional text tokens for detection")
235
+ # Detection configuration
236
+ brand_detection: BrandDetectionConfig = Field(default_factory=BrandDetectionConfig, description="Brand detection settings")
237
+ category_detection: CategoryDetectionConfig = Field(default_factory=CategoryDetectionConfig, description="Category detection settings")
238
+
239
+ # Shelf layout
240
+ shelves: List[ShelfConfig] = Field(description="Configuration for each shelf level")
241
+
242
+ # Advertisement configuration
243
+ advertisement_endcap: Optional[AdvertisementEndcap] = Field(default=None, description="Advertisement endcap configuration")
244
+
245
+ # Global compliance settings
246
+ global_compliance_threshold: float = Field(default=0.8, description="Default compliance threshold")
247
+ weighted_scoring: Dict[str, float] = Field(
248
+ default_factory=lambda: {"product_compliance": 0.7, "text_compliance": 0.3},
249
+ description="Weights for different compliance aspects"
250
+ )
251
+
252
+ # Additional metadata
253
+ planogram_id: Optional[str] = Field(default=None, description="Unique identifier for this planogram")
254
+ created_date: Optional[str] = Field(default=None, description="Creation date")
255
+ version: str = Field(default="1.0", description="Planogram version")
256
+ notes: Optional[str] = Field(default=None, description="Additional notes or instructions")
257
+
258
+
259
+ class PlanogramDescriptionFactory:
260
+ """Factory class for creating PlanogramDescription objects from dictionaries"""
261
+
262
+ @staticmethod
263
+ def create_planogram_description(config_dict: Dict[str, Any]) -> PlanogramDescription:
264
+ """
265
+ Create a PlanogramDescription object from a dictionary configuration.
266
+
267
+ Args:
268
+ config_dict: Dictionary containing all planogram configuration
269
+
270
+ Returns:
271
+ PlanogramDescription object ready for compliance checking
272
+
273
+ Example config_dict structure:
274
+ {
275
+ "brand": "Epson",
276
+ "category": "Printers",
277
+ "aisle": {
278
+ "name": "Electronics",
279
+ "category_hints": ["printers", "ink", "paper"],
280
+ "lighting_conditions": "normal"
281
+ },
282
+ "tags": ["goodbye", "hello", "savings", "cartridges"],
283
+ # Advertisement sizing and positioning
284
+ "advertisement": {
285
+ "width_percent": 0.45, # 45% of image width
286
+ "height_percent": 0.25, # 25% of image height
287
+ "top_margin_percent": 0.02, # 2% margin above detected brand
288
+ "side_margin_percent": 0.05 # 5% margin on sides
289
+ },
290
+ "brand_detection": {
291
+ "enabled": True,
292
+ "target_brands": ["Epson", "Canon", "HP"],
293
+ "confidence_threshold": 0.8
294
+ },
295
+ "category_detection": {
296
+ "enabled": True,
297
+ "target_categories": ["printer", "product_box", "promotional_graphic"],
298
+ "confidence_threshold": 0.7
299
+ },
300
+ "shelves": [
301
+ {
302
+ "level": "header",
303
+ "products": [
304
+ {
305
+ "name": "Epson EcoTank Advertisement",
306
+ "product_type": "promotional_graphic",
307
+ "mandatory": True
308
+ }
309
+ ],
310
+ "compliance_threshold": 0.9
311
+ },
312
+ {
313
+ "level": "top",
314
+ "products": [
315
+ {
316
+ "name": "ET-2980",
317
+ "product_type": "printer",
318
+ "quantity_range": [1, 2],
319
+ "position_preference": "left"
320
+ },
321
+ {
322
+ "name": "ET-3950",
323
+ "product_type": "printer",
324
+ "quantity_range": [1, 1],
325
+ "position_preference": "center"
326
+ }
327
+ ],
328
+ "compliance_threshold": 0.8
329
+ }
330
+ ],
331
+ "advertisement_endcap": {
332
+ "enabled": True,
333
+ "promotional_type": "backlit_graphic",
334
+ "position": "header",
335
+ "brand_requirements": ["Epson"],
336
+ "text_requirements": [
337
+ {
338
+ "required_text": "Goodbye Cartridges",
339
+ "match_type": "contains",
340
+ "mandatory": True
341
+ },
342
+ {
343
+ "required_text": "Hello Savings",
344
+ "match_type": "contains",
345
+ "mandatory": True
346
+ }
347
+ ]
348
+ }
349
+ }
350
+ """
351
+
352
+ # Process aisle configuration
353
+ aisle_data = config_dict.get("aisle", {})
354
+ if isinstance(aisle_data, str):
355
+ # Simple string aisle name
356
+ aisle_config = AisleConfig(name=aisle_data)
357
+ else:
358
+ # Full aisle configuration
359
+ aisle_config = AisleConfig(**aisle_data)
360
+
361
+ # Process brand detection configuration
362
+ brand_detection_data = config_dict.get("brand_detection", {})
363
+ brand_detection_config = BrandDetectionConfig(**brand_detection_data)
364
+
365
+ # Process category detection configuration
366
+ category_detection_data = config_dict.get("category_detection", {})
367
+ category_detection_config = CategoryDetectionConfig(**category_detection_data)
368
+
369
+ # Process shelves configuration
370
+ shelves_data = config_dict.get("shelves", [])
371
+ shelf_configs = []
372
+
373
+ for shelf_data in shelves_data:
374
+ # Process products for this shelf
375
+ products_data = shelf_data.get("products", [])
376
+ shelf_products = []
377
+
378
+ for product_data in products_data:
379
+ shelf_product = ShelfProduct(**product_data)
380
+ shelf_products.append(shelf_product)
381
+
382
+ # Create shelf config
383
+ shelf_config = ShelfConfig(
384
+ level=shelf_data["level"],
385
+ products=shelf_products,
386
+ compliance_threshold=shelf_data.get("compliance_threshold", 0.8),
387
+ allow_extra_products=shelf_data.get("allow_extra_products", False),
388
+ position_strict=shelf_data.get("position_strict", False)
389
+ )
390
+ shelf_configs.append(shelf_config)
391
+
392
+ # Process advertisement endcap configuration
393
+ advertisement_endcap = None
394
+ endcap_data = config_dict.get("advertisement_endcap")
395
+ if endcap_data:
396
+ # Process text requirements
397
+ text_requirements = []
398
+ for text_req_data in endcap_data.get("text_requirements", []):
399
+ text_req = TextRequirement(**text_req_data)
400
+ text_requirements.append(text_req)
401
+
402
+ # Update endcap data with processed text requirements
403
+ endcap_data = endcap_data.copy()
404
+ endcap_data["text_requirements"] = text_requirements
405
+
406
+ advertisement_endcap = AdvertisementEndcap(**endcap_data)
407
+
408
+ # Create the main PlanogramDescription object
409
+ planogram_description = PlanogramDescription(
410
+ brand=config_dict["brand"],
411
+ category=config_dict["category"],
412
+ aisle=aisle_config,
413
+ text_tokens=config_dict.get("text_tokens", []),
414
+ advertisement=config_dict.get("advertisement", {}),
415
+ tags=config_dict.get("tags", []),
416
+ brand_detection=brand_detection_config,
417
+ category_detection=category_detection_config,
418
+ shelves=shelf_configs,
419
+ advertisement_endcap=advertisement_endcap,
420
+ global_compliance_threshold=config_dict.get("global_compliance_threshold", 0.8),
421
+ weighted_scoring=config_dict.get("weighted_scoring", {"product_compliance": 0.7, "text_compliance": 0.3}),
422
+ planogram_id=config_dict.get("planogram_id"),
423
+ created_date=config_dict.get("created_date"),
424
+ version=config_dict.get("version", "1.0"),
425
+ notes=config_dict.get("notes")
426
+ )
427
+
428
+ return planogram_description
429
+
430
+ class PlanogramConfigBuilder:
431
+ """Builder class for easier construction of planogram configurations"""
432
+
433
+ def __init__(self):
434
+ self.config = {
435
+ "brand": "",
436
+ "category": "",
437
+ "aisle": {},
438
+ "shelves": [],
439
+ "global_compliance_threshold": 0.8
440
+ }
441
+
442
+ def set_basic_info(self, brand: str, category: str, aisle: str) -> "PlanogramConfigBuilder":
443
+ """Set basic planogram information"""
444
+ self.config["brand"] = brand
445
+ self.config["category"] = category
446
+ self.config["aisle"] = {"name": aisle}
447
+ return self
448
+
449
+ def add_shelf(
450
+ self,
451
+ level: str,
452
+ products: List[Dict[str, Any]],
453
+ compliance_threshold: float = 0.8
454
+ ) -> "PlanogramConfigBuilder":
455
+ """Add a shelf configuration"""
456
+ shelf_config = {
457
+ "level": level,
458
+ "products": products,
459
+ "compliance_threshold": compliance_threshold
460
+ }
461
+ self.config["shelves"].append(shelf_config)
462
+ return self
463
+
464
+ def add_product_to_shelf(
465
+ self,
466
+ shelf_level: str,
467
+ name: str,
468
+ product_type: str,
469
+ quantity_range: tuple = (1, 1),
470
+ mandatory: bool = True
471
+ ) -> "PlanogramConfigBuilder":
472
+ """Add a product to an existing shelf"""
473
+ # Find the shelf
474
+ for shelf in self.config["shelves"]:
475
+ if shelf["level"] == shelf_level:
476
+ product = {
477
+ "name": name,
478
+ "product_type": product_type,
479
+ "quantity_range": quantity_range,
480
+ "mandatory": mandatory
481
+ }
482
+ shelf["products"].append(product)
483
+ break
484
+ return self
485
+
486
+ def set_advertisement_endcap(
487
+ self,
488
+ promotional_type: str,
489
+ position: str = "header",
490
+ brand_requirements: List[str] = None,
491
+ text_requirements: List[Dict[str, Any]] = None
492
+ ) -> "PlanogramConfigBuilder":
493
+ """Configure advertisement endcap"""
494
+ endcap_config = {
495
+ "enabled": True,
496
+ "promotional_type": promotional_type,
497
+ "position": position,
498
+ "brand_requirements": brand_requirements or [],
499
+ "text_requirements": text_requirements or []
500
+ }
501
+ self.config["advertisement_endcap"] = endcap_config
502
+ return self
503
+
504
+ def set_brand_detection(
505
+ self,
506
+ target_brands: List[str],
507
+ confidence_threshold: float = 0.7
508
+ ) -> "PlanogramConfigBuilder":
509
+ """Configure brand detection"""
510
+ self.config["brand_detection"] = {
511
+ "enabled": True,
512
+ "target_brands": target_brands,
513
+ "confidence_threshold": confidence_threshold
514
+ }
515
+ return self
516
+
517
+ def build(self) -> Dict[str, Any]:
518
+ """Build the final configuration dictionary"""
519
+ return self.config
520
+
521
+
522
+ def _dump(obj) -> Dict[str, Any]:
523
+ """Works with Pydantic v1/v2 or plain dicts."""
524
+ if obj is None:
525
+ return {}
526
+ if hasattr(obj, "model_dump"):
527
+ return obj.model_dump(exclude_none=True)
528
+ if hasattr(obj, "dict"):
529
+ return obj.dict(exclude_none=True)
530
+ if isinstance(obj, dict):
531
+ return obj
532
+ # Fallback: shallow attr dump
533
+ return {
534
+ k: getattr(obj, k) for k in dir(obj) if not k.startswith("_") and not callable(getattr(obj, k))
535
+ }
536
+
537
+ def _product_label(p) -> str:
538
+ # ShelfProduct may be a pydantic model or dict
539
+ if p is None:
540
+ return "UNKNOWN"
541
+ if isinstance(p, dict):
542
+ name = p.get("name") or p.get("product_type") or "UNKNOWN"
543
+ return str(name)
544
+ name = getattr(p, "name", None) or getattr(p, "product_type", None) or "UNKNOWN"
545
+ return str(name)
546
+
547
+ def build_planogram_json_diagram(planogram) -> Dict[str, Any]:
548
+ """
549
+ Produce a compact, human-friendly JSON 'diagram' of a PlanogramDescription.
550
+ Keys/shape kept simple for rendering and reporting.
551
+ """
552
+ shelves: List[Dict[str, Any]] = []
553
+ for s in getattr(planogram, "shelves", []):
554
+ # Accept model or dict
555
+ s_d = _dump(s)
556
+ level = s_d.get("level") or s_d.get("name") or s_d.get("label") or "unknown"
557
+ products = s_d.get("products", [])
558
+ expected = [_product_label(p) for p in products]
559
+ shelves.append({
560
+ "shelf": str(level),
561
+ "slots": len(products),
562
+ "expected_left_to_right": expected,
563
+ "compliance_threshold": s_d.get(
564
+ "compliance_threshold", getattr(planogram, "global_compliance_threshold", 0.8)
565
+ ),
566
+ "allow_extra_products": s_d.get("allow_extra_products", False),
567
+ "position_strict": s_d.get("position_strict", False),
568
+ "notes": s_d.get("notes"),
569
+ })
570
+
571
+ ad = _dump(getattr(planogram, "advertisement_endcap", None)) or None
572
+ aisle = _dump(getattr(planogram, "aisle", None))
573
+
574
+ diagram: Dict[str, Any] = {
575
+ "brand": getattr(planogram, "brand", ""),
576
+ "category": getattr(planogram, "category", ""),
577
+ "aisle": aisle,
578
+ "advertisement_endcap": ad, # will be None if not configured
579
+ "shelves": shelves,
580
+ "global_compliance_threshold": getattr(planogram, "global_compliance_threshold", 0.8),
581
+ "weighted_scoring": dict(getattr(planogram, "weighted_scoring", {"product_compliance": 0.7, "text_compliance": 0.3})),
582
+ "metadata": {
583
+ "planogram_id": getattr(planogram, "planogram_id", None),
584
+ "created_date": getattr(planogram, "created_date", None),
585
+ "version": getattr(planogram, "version", "1.0"),
586
+ "notes": getattr(planogram, "notes", None),
587
+ },
588
+ }
589
+ return diagram
590
+
591
+ def planogram_diagram_to_markdown(diagram: Mapping[str, Any]) -> str:
592
+ """Render the JSON diagram as Markdown ready for reports."""
593
+ brand = diagram.get("brand", "")
594
+ category = diagram.get("category", "")
595
+ gthr = diagram.get("global_compliance_threshold", "")
596
+ weights = diagram.get("weighted_scoring", {})
597
+ meta = diagram.get("metadata", {})
598
+ aisle = diagram.get("aisle", {})
599
+ ad = diagram.get("advertisement_endcap", None)
600
+ shelves = diagram.get("shelves", [])
601
+
602
+ def _fmt_dict(d: Mapping[str, Any]) -> str:
603
+ if not d:
604
+ return "-"
605
+ # compact key: value list
606
+ parts = []
607
+ for k, v in d.items():
608
+ if isinstance(v, (list, dict)):
609
+ parts.append(f"**{k}**: `{str(v)}`")
610
+ else:
611
+ parts.append(f"**{k}**: {v}")
612
+ return "<br>".join(parts)
613
+
614
+ # Header table
615
+ md = []
616
+ md.append("| Field | Value |")
617
+ md.append("|---|---|")
618
+ md.append(f"| **Brand** | {brand} |")
619
+ md.append(f"| **Category** | {category} |")
620
+ md.append(f"| **Global Threshold** | {gthr} |")
621
+ md.append(f"| **Weighted Scoring** | product: {weights.get('product_compliance', '-')}, text: {weights.get('text_compliance', '-')} |")
622
+ md.append(f"| **Planogram ID** | {meta.get('planogram_id','-')} |")
623
+ md.append(f"| **Version** | {meta.get('version','-')} |")
624
+ md.append(f"| **Created** | {meta.get('created_date','-')} |")
625
+ md.append(f"| **Notes** | {meta.get('notes','-')} |")
626
+ md.append("")
627
+ # Aisle block
628
+ md.append("**Aisle**")
629
+ md.append("")
630
+ md.append(_fmt_dict(aisle))
631
+ md.append("")
632
+ # Advertisement block
633
+ md.append("**Advertisement Endcap**")
634
+ md.append("")
635
+ if ad:
636
+ md.append(_fmt_dict(ad))
637
+ else:
638
+ md.append("_None_")
639
+ md.append("")
640
+ # Shelves table
641
+ if shelves:
642
+ md.append("**Shelves**")
643
+ md.append("")
644
+ md.append("| Shelf | Slots | Expected (L→R) | Threshold | Allow Extra | Position Strict | Notes |")
645
+ md.append("|---:|---:|---|---:|:---:|:---:|---|")
646
+ for s in shelves:
647
+ exp = ", ".join(str(x) for x in s.get("expected_left_to_right", []))
648
+ md.append(
649
+ f"| {s.get('shelf','-')} | {s.get('slots','-')} | `{exp}` | "
650
+ f"{s.get('compliance_threshold','-')} | {s.get('allow_extra_products', False)} | "
651
+ f"{s.get('position_strict', False)} | {s.get('notes','-')} |"
652
+ )
653
+ md.append("")
654
+ return "\n".join(md)