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,436 @@
1
+ from typing import Any, Optional
2
+ from abc import abstractmethod
3
+ import re
4
+ import html
5
+ import uuid
6
+ from pathlib import Path
7
+ from pygments import highlight
8
+ from pygments.lexers.python import PythonLexer
9
+ from pygments.formatters.html import HtmlFormatter
10
+
11
+ try:
12
+ from ipywidgets import HTML as IPyHTML
13
+ IPYWIDGETS_AVAILABLE = True
14
+ except ImportError:
15
+ IPYWIDGETS_AVAILABLE = False
16
+
17
+ from .base import BaseRenderer
18
+
19
+
20
+ class BaseChart(BaseRenderer):
21
+ """Base class for chart renderers - extends BaseRenderer with chart-specific methods"""
22
+
23
+ @staticmethod
24
+ def _extract_code(content: str) -> Optional[str]:
25
+ """Extract Python code from markdown blocks."""
26
+ pattern = r'```(?:python)?\n(.*?)```'
27
+ matches = re.findall(pattern, content, re.DOTALL)
28
+ return matches[0].strip() if matches else None
29
+
30
+ @staticmethod
31
+ def _highlight_code(code: str, theme: str = 'monokai') -> str:
32
+ """Apply syntax highlighting to code."""
33
+ try:
34
+ formatter = HtmlFormatter(style=theme, noclasses=True, cssclass='code')
35
+ return highlight(code, PythonLexer(), formatter)
36
+ except ImportError:
37
+ escaped = html.escape(code)
38
+ return f'<pre class="code"><code>{escaped}</code></pre>'
39
+
40
+ @staticmethod
41
+ def _wrap_for_environment(content: Any, environment: str) -> Any:
42
+ """Wrap content based on environment."""
43
+ if isinstance(content, str) and environment in {'jupyter', 'ipython', 'colab'} and IPYWIDGETS_AVAILABLE:
44
+ return IPyHTML(value=content)
45
+ return content
46
+
47
+ @staticmethod
48
+ def _build_code_section(code: str, theme: str, icon: str = "📊") -> str:
49
+ """Build collapsible code section."""
50
+ highlighted = BaseChart._highlight_code(code, theme)
51
+ return f'''
52
+ <details class="ap-code-accordion">
53
+ <summary class="ap-code-header">
54
+ <span>{icon} View Code</span>
55
+ <span class="ap-toggle-icon">▶</span>
56
+ </summary>
57
+ <div class="ap-code-content">
58
+ {highlighted}
59
+ </div>
60
+ </details>
61
+ '''
62
+
63
+ @staticmethod
64
+ def _render_error(error: str, code: str, theme: str) -> str:
65
+ """Render error message with code."""
66
+ highlighted = BaseChart._highlight_code(code, theme)
67
+ return f'''
68
+ {BaseChart._get_chart_styles()}
69
+ <div class="ap-error-container">
70
+ <h3>⚠️ Chart Generation Error</h3>
71
+ <p class="ap-error-message">{error}</p>
72
+ <details class="ap-code-accordion" open>
73
+ <summary class="ap-code-header">Code with Error</summary>
74
+ <div class="ap-code-content">{highlighted}</div>
75
+ </details>
76
+ </div>
77
+ '''
78
+
79
+ @staticmethod
80
+ def _get_chart_styles() -> str:
81
+ """CSS styles specific to charts."""
82
+ return '''
83
+ <style>
84
+ .ap-chart-container {
85
+ background: white;
86
+ border-radius: 8px;
87
+ }
88
+ .ap-chart-wrapper {
89
+ min-height: 400px;
90
+ justify-content: center;
91
+ align-items: center;
92
+ }
93
+ .ap-chart-guidance {
94
+ background: #f0f4ff;
95
+ border-left: 4px solid #667eea;
96
+ padding: 16px 20px;
97
+ border-radius: 6px;
98
+ }
99
+ .ap-chart-guidance h3 {
100
+ font-size: 16px;
101
+ font-weight: 600;
102
+ color: #364152;
103
+ }
104
+ .ap-chart-guidance ol {
105
+ margin: 0 0 0 20px;
106
+ padding: 0;
107
+ }
108
+ .ap-chart-guidance li {
109
+ margin-bottom: 6px;
110
+ line-height: 1.4;
111
+ }
112
+ .ap-chart-note {
113
+ background: #fffaf0;
114
+ border-left: 4px solid #f6ad55;
115
+ padding: 2px 4px;
116
+ border-radius: 6px;
117
+ margin-bottom: 2px;
118
+ color: #744210;
119
+ font-size: 14px;
120
+ }
121
+ .ap-code-accordion {
122
+ margin-top: 20px;
123
+ border: 1px solid #e0e0e0;
124
+ border-radius: 6px;
125
+ overflow: hidden;
126
+ }
127
+ .ap-code-header {
128
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
129
+ color: white;
130
+ padding: 12px 20px;
131
+ cursor: pointer;
132
+ display: flex;
133
+ justify-content: space-between;
134
+ align-items: center;
135
+ font-weight: 600;
136
+ user-select: none;
137
+ }
138
+ .ap-code-header:hover {
139
+ background: linear-gradient(135deg, #5568d3 0%, #653a8e 100%);
140
+ }
141
+ .ap-toggle-icon {
142
+ transition: transform 0.3s ease;
143
+ }
144
+ details[open] .ap-toggle-icon {
145
+ transform: rotate(90deg);
146
+ }
147
+ .ap-code-content {
148
+ background: #272822;
149
+ padding: 15px;
150
+ overflow-x: auto;
151
+ }
152
+ .ap-code-content pre {
153
+ margin: 0;
154
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
155
+ font-size: 13px;
156
+ line-height: 1.5;
157
+ }
158
+ .ap-error-container {
159
+ background: #fff3cd;
160
+ border: 1px solid #ffc107;
161
+ border-radius: 8px;
162
+ padding: 20px;
163
+ margin: 20px 0;
164
+ }
165
+ .ap-error-message {
166
+ color: #856404;
167
+ font-weight: 500;
168
+ margin: 10px 0;
169
+ }
170
+ </style>
171
+ '''
172
+
173
+ def _save_to_disk(self, chart_obj: Any, filename: str = None) -> str:
174
+ """Save chart to disk for terminal viewing."""
175
+ if not filename:
176
+ filename = f"chart_{uuid.uuid4().hex[:8]}.png"
177
+
178
+ # Ensure we have a directory
179
+ # TODO: using a fixed path for now; ideally this should be configurable
180
+ output_dir = Path("outputs/charts")
181
+ output_dir.mkdir(parents=True, exist_ok=True)
182
+
183
+ filepath = output_dir / filename
184
+ chart_obj.savefig(str(filepath), bbox_inches='tight', dpi=100)
185
+ return str(filepath)
186
+
187
+ @staticmethod
188
+ def _build_html_document(
189
+ chart_content: str,
190
+ code_section: str = '',
191
+ title: str = 'AI-Parrot Chart',
192
+ extra_head: str = '',
193
+ mode: str = 'partial'
194
+ ) -> str:
195
+ """Build HTML document wrapper for charts."""
196
+ if mode == 'partial':
197
+ return f'''
198
+ {BaseChart._get_chart_styles()}
199
+ <div class="ap-chart-container">
200
+ <div class="ap-chart-wrapper">
201
+ {chart_content}
202
+ </div>
203
+ </div>
204
+ {code_section}
205
+ '''
206
+
207
+ elif mode == 'complete':
208
+ # Generate unique ID for the container
209
+ container_id = f"container-{uuid.uuid4().hex[:8]}"
210
+
211
+ return f'''<!DOCTYPE html>
212
+ <html lang="en">
213
+ <head>
214
+ <meta charset="UTF-8">
215
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
216
+ <title>{title}</title>
217
+
218
+ {extra_head}
219
+
220
+ <style>
221
+ .ap-chart-wrapper {{
222
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
223
+ 'Helvetica Neue', Arial, sans-serif;
224
+ padding: 5px;
225
+ line-height: 1.6;
226
+ box-sizing: border-box;
227
+ }}
228
+
229
+ .ap-chart-wrapper * {{
230
+ box-sizing: border-box;
231
+ }}
232
+
233
+ .ap-media-container {{
234
+ max-width: 1200px;
235
+ margin: 0 auto;
236
+ }}
237
+
238
+ .ap-chart-container {{
239
+ border-radius: 12px;
240
+ padding: 5px;
241
+ }}
242
+
243
+ .ap-chart-wrapper {{
244
+ min-height: 400px;
245
+ display: flex;
246
+ justify-content: center;
247
+ align-items: center;
248
+ }}
249
+
250
+ .ap-code-accordion {{
251
+ margin-top: 20px;
252
+ border: 1px solid #e0e0e0;
253
+ border-radius: 8px;
254
+ overflow: hidden;
255
+ background: white;
256
+ }}
257
+
258
+ .ap-code-header {{
259
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
260
+ color: white;
261
+ padding: 14px 20px;
262
+ cursor: pointer;
263
+ display: flex;
264
+ justify-content: space-between;
265
+ align-items: center;
266
+ font-weight: 600;
267
+ user-select: none;
268
+ transition: all 0.3s ease;
269
+ }}
270
+
271
+ .ap-code-header:hover {{
272
+ background: linear-gradient(135deg, #5568d3 0%, #653a8e 100%);
273
+ }}
274
+
275
+ .ap-toggle-icon {{
276
+ transition: transform 0.3s ease;
277
+ font-size: 12px;
278
+ }}
279
+
280
+ details[open] .ap-toggle-icon {{
281
+ transform: rotate(90deg);
282
+ }}
283
+
284
+ .ap-code-content {{
285
+ background: #272822;
286
+ padding: 20px;
287
+ overflow-x: auto;
288
+ }}
289
+
290
+ .ap-code-content pre {{
291
+ margin: 0;
292
+ font-family: 'Monaco', 'Menlo', 'Consolas', 'Courier New', monospace;
293
+ font-size: 14px;
294
+ line-height: 1.6;
295
+ }}
296
+
297
+ .ap-error-container {{
298
+ background: #fff3cd;
299
+ border: 2px solid #ffc107;
300
+ border-radius: 8px;
301
+ padding: 20px;
302
+ margin: 20px 0;
303
+ }}
304
+
305
+ .ap-error-container h3 {{
306
+ color: #856404;
307
+ margin-bottom: 10px;
308
+ }}
309
+
310
+ .ap-error-message {{
311
+ color: #856404;
312
+ font-weight: 500;
313
+ margin: 10px 0;
314
+ }}
315
+
316
+ @media (max-width: 768px) {{
317
+ .ap-chart-wrapper {{
318
+ padding: 1px;
319
+ }}
320
+
321
+ .ap-chart-container {{
322
+ padding: 2px;
323
+ }}
324
+ }}
325
+ </style>
326
+ </head>
327
+ <body>
328
+ <div class="ap-chart-wrapper">
329
+ <div class="ap-media-container" id="{container_id}">
330
+ <div class="ap-chart-container">
331
+ <div class="ap-chart-wrapper">
332
+ {chart_content}
333
+ </div>
334
+ </div>
335
+
336
+ {code_section}
337
+ </div>
338
+ </div>
339
+
340
+ <script>
341
+ (function() {{
342
+ function sendSize() {{
343
+ const width = document.documentElement.scrollWidth || window.innerWidth;
344
+ const height = document.documentElement.scrollHeight || window.innerHeight;
345
+
346
+ parent.postMessage(
347
+ {{
348
+ type: "iframe_resize",
349
+ width: width,
350
+ height: height,
351
+ containerId: "{container_id}"
352
+ }},
353
+ "*"
354
+ );
355
+ }}
356
+
357
+ // Send initial size
358
+ sendSize();
359
+
360
+ // Detect viewport resize
361
+ window.addEventListener("resize", sendSize);
362
+
363
+ // Detect content changes (e.g., height changes from DOM mutations)
364
+ const observer = new ResizeObserver(sendSize);
365
+ observer.observe(document.body);
366
+
367
+ // Also observe the container for more precise tracking
368
+ const container = document.getElementById("{container_id}");
369
+ if (container) {{
370
+ observer.observe(container);
371
+ }}
372
+ }})();
373
+ </script>
374
+ </body>
375
+ </html>'''
376
+
377
+ else:
378
+ raise ValueError(f"Invalid mode: {mode}. Must be 'partial' or 'complete'")
379
+
380
+ @abstractmethod
381
+ def _render_chart_content(self, chart_obj: Any, **kwargs) -> str:
382
+ """
383
+ Render the chart-specific content (to be embedded in HTML).
384
+ This should return just the chart div/script, not the full HTML document.
385
+
386
+ Each chart renderer must implement this method to generate their
387
+ specific chart content (Altair vega-embed, Plotly div, etc.)
388
+ """
389
+ pass
390
+
391
+ def to_html(
392
+ self,
393
+ chart_obj: Any,
394
+ mode: str = 'partial',
395
+ include_code: bool = False,
396
+ code: Optional[str] = None,
397
+ theme: str = 'monokai',
398
+ title: str = 'AI-Parrot Chart',
399
+ **kwargs
400
+ ) -> str:
401
+ """
402
+ Convert chart object to HTML.
403
+
404
+ Args:
405
+ chart_obj: Chart object to render
406
+ mode: 'partial' for embeddable HTML or 'complete' for full document
407
+ include_code: Whether to include code section
408
+ code: Python code to display
409
+ theme: Code highlighting theme
410
+ title: Document title (for complete mode)
411
+ **kwargs: Additional parameters passed to _render_chart_content
412
+
413
+ Returns:
414
+ HTML string based on mode
415
+ """
416
+ # Get chart-specific content from subclass
417
+ chart_content = self._render_chart_content(chart_obj, **kwargs)
418
+
419
+ # Build code section if requested
420
+ code_section = ''
421
+ if include_code and code:
422
+ code_section = self._build_code_section(
423
+ code, theme, kwargs.get('icon', '📊')
424
+ )
425
+
426
+ # Get extra head content if provided by subclass
427
+ extra_head = kwargs.get('extra_head', '')
428
+
429
+ # Build final HTML
430
+ return self._build_html_document(
431
+ chart_content=chart_content,
432
+ code_section=code_section,
433
+ title=title,
434
+ extra_head=extra_head,
435
+ mode=mode
436
+ )
@@ -0,0 +1,255 @@
1
+ # ai_parrot/outputs/formats/charts/d3.py
2
+ import contextlib
3
+ from typing import Any, Optional, Tuple, Dict, List
4
+ import json
5
+ import html
6
+ import re
7
+ import uuid
8
+ from .base import BaseChart
9
+ from . import register_renderer
10
+ from ...models.outputs import OutputMode
11
+
12
+
13
+ D3_SYSTEM_PROMPT = """🚨 CRITICAL – OUTPUT FORMAT IS STRICT 🚨
14
+
15
+ You are generating **text only** — specifically a single fenced ```json block that contains:
16
+ - "teaching_steps": array of 3–6 short bullets.
17
+ - "javascript": string with **D3 code only** (newlines escaped as \\n).
18
+ - Optional: "notes": short string.
19
+
20
+ ABSOLUTE RULES
21
+ 1) Do **NOT** use any fence other than ```json. Never use ```javascript.
22
+ 2) The "javascript" must target **'#chart'** and assume **global d3** (no imports/exports).
23
+ 3) **No HTML/CSS**: no <!DOCTYPE>, <html>, <head>, <body>, <script>, <style>, <link>.
24
+ 4) Do not include external scripts, ESM imports, require(), module, etc.
25
+ 5) If you think HTML is required: **STOP** and instead return the JSON with only D3 code.
26
+
27
+ 🚫 If your output contains any of these substrings, it will be rejected:
28
+ "<!DOCTYPE", "<html", "<head", "<body", "<script", "<link", "<style", "import ", "export ", "require("
29
+
30
+ ✅ EXAMPLE (illustrative):
31
+ ```json
32
+ {
33
+ "teaching_steps": [
34
+ "Append an SVG into #chart with margins.",
35
+ "Create time and linear scales.",
36
+ "Render axes and a line path."
37
+ ],
38
+ "javascript": "const margin={top:20,right:20,bottom:30,left:40};\\nconst width=640,height=400;\\nconst svg=d3.select('#chart').append('svg').attr('width',width).attr('height',height);\\n/* more d3 code here */",
39
+ "notes": "Optional clarifications."
40
+ }
41
+ ```
42
+ Remember:
43
+ * Container is always '#chart'.
44
+ * Use the global d3 (no imports).
45
+ * Provide complete, runnable D3 code only (no HTML or script tags).
46
+ """
47
+
48
+
49
+ @register_renderer(OutputMode.D3, system_prompt=D3_SYSTEM_PROMPT)
50
+ class D3Renderer(BaseChart):
51
+ """Renderer for D3.js visualizations"""
52
+
53
+ def execute_code(
54
+ self,
55
+ code: str,
56
+ pandas_tool: "PythonPandasTool | None" = None,
57
+ execution_state: Optional[Dict[str, Any]] = None,
58
+ **kwargs,
59
+ ) -> Tuple[Any, Optional[str]]:
60
+ """
61
+ For D3, we don't execute JavaScript - just validate and return it.
62
+ """
63
+ try:
64
+ # Basic validation - check if it looks like D3 code
65
+ if 'd3.' not in code and 'D3' not in code:
66
+ return None, "Code doesn't appear to use D3.js (no d3. references found)"
67
+
68
+ # Return the code itself as the "chart object"
69
+ return code, None
70
+
71
+ except Exception as e:
72
+ return None, f"Validation error: {str(e)}"
73
+
74
+ def _render_chart_content(
75
+ self,
76
+ chart_obj: Any,
77
+ teaching_steps: Optional[List[str]] = None,
78
+ notes: Optional[str] = None,
79
+ **kwargs
80
+ ) -> str:
81
+ """Render D3 visualization content."""
82
+ # chart_obj is the JavaScript code
83
+ js_code = chart_obj
84
+ chart_id = f"d3-chart-{uuid.uuid4().hex[:8]}"
85
+
86
+ # Replace '#chart' with our specific chart ID
87
+ js_code = js_code.replace("'#chart'", f"'#{chart_id}'")
88
+ js_code = js_code.replace('"#chart"', f'"#{chart_id}"')
89
+ js_code = js_code.replace('`#chart`', f'`#{chart_id}`')
90
+
91
+ guidance_sections: List[str] = []
92
+ if teaching_steps:
93
+ items = ''.join(f"<li>{html.escape(step)}</li>" for step in teaching_steps)
94
+ guidance_sections.append(
95
+ f"""
96
+ <div class=\"ap-chart-guidance\">
97
+ <h3>How this chart works</h3>
98
+ <ol>{items}</ol>
99
+ </div>
100
+ """
101
+ )
102
+
103
+ if notes:
104
+ guidance_sections.append(
105
+ f"""
106
+ <div class=\"ap-chart-note\"><strong>Notes:</strong> {html.escape(notes)}</div>
107
+ """
108
+ )
109
+
110
+ guidance_html = '\n'.join(guidance_sections)
111
+
112
+ return f'''
113
+ {guidance_html}
114
+ <div id="{chart_id}" style="width: 100%; min-height: 400px;"></div>
115
+ <script type="text/javascript">
116
+ (function() {{
117
+ {js_code}
118
+ }})();
119
+ </script>
120
+ '''
121
+
122
+ def to_html(
123
+ self,
124
+ chart_obj: Any,
125
+ mode: str = 'partial',
126
+ **kwargs
127
+ ) -> str:
128
+ """Convert D3 visualization to HTML."""
129
+ # D3.js library for <head>
130
+ d3_version = kwargs.get('d3_version', '7')
131
+ extra_head = f'''
132
+ <!-- D3.js -->
133
+ <script src="https://d3js.org/d3.v{d3_version}.min.js"></script>
134
+ '''
135
+
136
+ kwargs['extra_head'] = extra_head
137
+
138
+ # Call parent to_html
139
+ return super().to_html(chart_obj, mode=mode, **kwargs)
140
+
141
+ def to_json(self, chart_obj: Any) -> Optional[Dict]:
142
+ """D3 code doesn't have a JSON representation."""
143
+ return {
144
+ 'type': 'd3_visualization',
145
+ 'code_length': len(chart_obj),
146
+ 'note': 'D3 visualizations are JavaScript code, not JSON data'
147
+ }
148
+
149
+ async def render(
150
+ self,
151
+ response: Any,
152
+ theme: str = 'monokai',
153
+ environment: str = 'terminal',
154
+ export_format: str = 'html',
155
+ include_code: bool = False,
156
+ html_mode: str = 'partial',
157
+ **kwargs
158
+ ) -> Tuple[Any, Optional[Any]]:
159
+ """Render D3 visualization."""
160
+ content = self._get_content(response)
161
+
162
+ # Extract payload containing instructions and JavaScript code
163
+ payload = self._extract_payload(content)
164
+
165
+ if not payload:
166
+ error_msg = (
167
+ "No D3 payload found in response (expected ```json block with a \"javascript\" field)"
168
+ )
169
+ error_html = "<div class='error'>Missing D3 JSON payload in response</div>"
170
+ return error_msg, error_html
171
+
172
+ code = payload['javascript']
173
+ teaching_steps = payload.get('teaching_steps', [])
174
+ notes = payload.get('notes')
175
+
176
+ # "Execute" (validate) code
177
+ js_code, error = self.execute_code(code)
178
+
179
+ if error:
180
+ error_html = self._render_error(error, code, theme)
181
+ return code, error_html
182
+
183
+ # Generate HTML
184
+ render_kwargs = dict(kwargs)
185
+ title = render_kwargs.pop('title', 'D3 Visualization')
186
+ render_kwargs['teaching_steps'] = teaching_steps
187
+ if notes:
188
+ render_kwargs['notes'] = notes
189
+
190
+ html_output = self.to_html(
191
+ js_code,
192
+ mode=html_mode,
193
+ include_code=include_code,
194
+ code=code,
195
+ theme=theme,
196
+ title=title,
197
+ icon='📊',
198
+ **render_kwargs
199
+ )
200
+
201
+ # Return (code, html)
202
+ return code, html_output
203
+
204
+ @staticmethod
205
+ def _extract_payload(content: str) -> Optional[Dict[str, Any]]:
206
+ """Extract a D3 payload from raw JSON, ```json fences, ```javascript fences, or HTML <script> tags."""
207
+ # 0) Fast path: raw JSON body (no fences)
208
+ with contextlib.suppress(Exception):
209
+ raw = json.loads(content)
210
+ if isinstance(raw, dict):
211
+ js = raw.get("javascript") or raw.get("code")
212
+ if isinstance(js, str) and js.strip():
213
+ steps = raw.get("teaching_steps") or []
214
+ notes = raw.get("notes")
215
+ return {
216
+ "javascript": js.strip(),
217
+ "teaching_steps": [str(s).strip() for s in steps if str(s).strip()] if isinstance(steps, list) else [],
218
+ "notes": notes.strip() if isinstance(notes, str) else None,
219
+ }
220
+
221
+ # 1) Markdown ```json fences (case-insensitive)
222
+ for block in re.findall(r'```json\s*(.*?)```', content, re.DOTALL | re.IGNORECASE):
223
+ try:
224
+ obj = json.loads(block)
225
+ except Exception:
226
+ continue
227
+ if isinstance(obj, dict):
228
+ js = obj.get("javascript") or obj.get("code")
229
+ if isinstance(js, str) and js.strip():
230
+ steps = obj.get("teaching_steps") or []
231
+ notes = obj.get("notes")
232
+ return {
233
+ "javascript": js.strip(),
234
+ "teaching_steps": [str(s).strip() for s in steps if str(s).strip()] if isinstance(steps, list) else [],
235
+ "notes": notes.strip() if isinstance(notes, str) else None,
236
+ }
237
+
238
+ # 2) Markdown ```javascript / ```js fences → salvage as JS
239
+ for js in re.findall(r'```(?:javascript|js)\s*(.*?)```', content, re.DOTALL | re.IGNORECASE):
240
+ js = js.strip()
241
+ if not js:
242
+ continue
243
+ # If a full HTML doc was embedded in the fence, extract inner <script> contents
244
+ if "<script" in js or "<!DOCTYPE" in js or "<html" in js:
245
+ scripts = re.findall(r'<script[^>]*>([\s\S]*?)</script>', js, re.IGNORECASE)
246
+ js = "\n".join(s.strip() for s in scripts if s.strip()) or js
247
+ return {"javascript": js, "teaching_steps": [], "notes": None}
248
+
249
+ # 3) Raw HTML with <script>…</script>
250
+ if "<script" in content:
251
+ scripts = re.findall(r'<script[^>]*>([\s\S]*?)</script>', content, re.IGNORECASE)
252
+ if js := "\n".join(s.strip() for s in scripts if s.strip()):
253
+ return {"javascript": js, "teaching_steps": [], "notes": None}
254
+
255
+ return None