writer 0.8.3rc4__py3-none-any.whl → 1.25.1rc1__py3-none-any.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 (378) hide show
  1. writer/__init__.py +1 -1
  2. writer/abstract.py +1 -1
  3. writer/{ai.py → ai/__init__.py} +867 -163
  4. writer/app_runner.py +596 -241
  5. writer/app_templates/default/.wf/components-blueprints_blueprint-0-0decp3w5erhvl0nw.jsonl +11 -0
  6. writer/app_templates/default/.wf/components-blueprints_root.jsonl +1 -0
  7. writer/app_templates/default/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl +27 -0
  8. writer/app_templates/default/.wf/components-root.jsonl +1 -0
  9. writer/app_templates/default/.wf/components-workflows_root.jsonl +1 -0
  10. writer/app_templates/default/.wf/components-workflows_workflow-0-lfltcky7l1fsm6j2.jsonl +1 -0
  11. writer/app_templates/default/.wf/metadata.json +3 -0
  12. writer/app_templates/default/README.md +3 -0
  13. writer/app_templates/default/main.py +16 -0
  14. writer/app_templates/default/requirements.txt +1 -0
  15. writer/app_templates/default/static/README.md +8 -0
  16. writer/app_templates/default/static/agent_builder_demo.png +0 -0
  17. writer/app_templates/default/static/favicon.png +0 -0
  18. writer/app_templates/hello/.wf/components-blueprints_blueprint-0-t84xyhxau9ej3823.jsonl +18 -0
  19. writer/app_templates/hello/.wf/components-blueprints_root.jsonl +1 -0
  20. writer/app_templates/hello/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl +15 -0
  21. writer/app_templates/hello/.wf/components-root.jsonl +1 -0
  22. writer/app_templates/hello/.wf/metadata.json +3 -0
  23. writer/app_templates/hello/main.py +16 -0
  24. writer/app_templates/hello/static/README.md +8 -0
  25. writer/app_templates/hello/static/favicon.png +0 -0
  26. writer/app_templates/hello/static/welcome.svg +40 -0
  27. writer/auth.py +7 -2
  28. writer/autogen.py +352 -0
  29. writer/blocks/__init__.py +51 -17
  30. writer/blocks/addtostatelist.py +10 -9
  31. writer/blocks/apitrigger.py +45 -0
  32. writer/blocks/base_block.py +332 -21
  33. writer/blocks/base_trigger.py +14 -0
  34. writer/blocks/calleventhandler.py +39 -35
  35. writer/blocks/changepage.py +48 -0
  36. writer/blocks/code.py +102 -0
  37. writer/blocks/crontrigger.py +49 -0
  38. writer/blocks/foreach.py +70 -53
  39. writer/blocks/httprequest.py +112 -99
  40. writer/blocks/ifelse.py +71 -0
  41. writer/blocks/logmessage.py +34 -39
  42. writer/blocks/parsejson.py +30 -29
  43. writer/blocks/returnvalue.py +7 -7
  44. writer/blocks/runblueprint.py +63 -0
  45. writer/blocks/setstate.py +43 -33
  46. writer/blocks/sharedblueprint.py +86 -0
  47. writer/blocks/uieventtrigger.py +49 -0
  48. writer/blocks/writeraddchatmessage.py +50 -12
  49. writer/blocks/writeraddtokg.py +38 -11
  50. writer/blocks/writeraskkg.py +123 -0
  51. writer/blocks/writerchat.py +80 -61
  52. writer/blocks/writerchatreply.py +279 -0
  53. writer/blocks/writerchatreplywithtoolconfig.py +393 -0
  54. writer/blocks/writerclassification.py +78 -39
  55. writer/blocks/writercompletion.py +49 -44
  56. writer/blocks/writerfileapi.py +85 -0
  57. writer/blocks/writerinitchat.py +24 -12
  58. writer/blocks/writerkeyvaluestorage.py +106 -0
  59. writer/blocks/writernocodeapp.py +35 -37
  60. writer/blocks/writerparsepdf.py +73 -0
  61. writer/blocks/writerstructuredoutput.py +105 -0
  62. writer/blocks/writertoolcalling.py +251 -0
  63. writer/blocks/writervision.py +141 -0
  64. writer/blocks/writerwebsearch.py +175 -0
  65. writer/blueprints.py +839 -0
  66. writer/command_line.py +52 -16
  67. writer/core.py +562 -290
  68. writer/core_ui.py +6 -2
  69. writer/evaluator.py +98 -46
  70. writer/journal.py +227 -0
  71. writer/keyvalue_storage.py +93 -0
  72. writer/logs.py +277 -0
  73. writer/serve.py +625 -327
  74. writer/ss_types.py +101 -12
  75. writer/static/assets/Arrow.dom-GBJpMYQS.js +1 -0
  76. writer/static/assets/BaseMarkdown-Wrvby5J8.js +1 -0
  77. writer/static/assets/BlueprintToolbar-BuXNRxWT.js +1 -0
  78. writer/static/assets/BlueprintToolbar-wpfX0jo_.css +1 -0
  79. writer/static/assets/BuilderApp-PTOI76jZ.js +8 -0
  80. writer/static/assets/BuilderApp-WimUfNZr.css +1 -0
  81. writer/static/assets/BuilderApplicationSelect-DXzy4e_h.js +7 -0
  82. writer/static/assets/BuilderApplicationSelect-XaM1D5fv.css +1 -0
  83. writer/static/assets/BuilderBlueprintLibraryPanel-Ckrhknlh.css +1 -0
  84. writer/static/assets/BuilderBlueprintLibraryPanel-DBDzhTlc.js +1 -0
  85. writer/static/assets/BuilderEmbeddedCodeEditor-B0bcjlhk.css +1 -0
  86. writer/static/assets/BuilderEmbeddedCodeEditor-Dn7eDICN.js +726 -0
  87. writer/static/assets/BuilderGraphSelect-C-LRsO8W.js +7 -0
  88. writer/static/assets/BuilderGraphSelect-D7B61d5s.css +1 -0
  89. writer/static/assets/BuilderInsertionLabel-BhyL9wgn.js +1 -0
  90. writer/static/assets/BuilderInsertionLabel-_YS5WPfq.css +1 -0
  91. writer/static/assets/BuilderInsertionOverlay-D2XS0ij9.css +1 -0
  92. writer/static/assets/BuilderInsertionOverlay-MkAIVruY.js +1 -0
  93. writer/static/assets/BuilderJournal-A0LcEwGI.js +7 -0
  94. writer/static/assets/BuilderJournal-DHv3Pvvm.css +1 -0
  95. writer/static/assets/BuilderModelSelect-CdSo_sih.js +7 -0
  96. writer/static/assets/BuilderModelSelect-Dc4IPLp2.css +1 -0
  97. writer/static/assets/BuilderSettings-BDwZBveu.js +16 -0
  98. writer/static/assets/BuilderSettings-lZkOXEYw.css +1 -0
  99. writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-3O6jKBXD.js +4 -0
  100. writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-DnX66iRg.css +1 -0
  101. writer/static/assets/BuilderSettingsDeploySharedBlueprint-BR_3ptsd.js +1 -0
  102. writer/static/assets/BuilderSettingsDeploySharedBlueprint-KJTl8gxP.css +1 -0
  103. writer/static/assets/BuilderSettingsHandlers-CBtEQFSo.js +1 -0
  104. writer/static/assets/BuilderSettingsHandlers-DJPeASfz.css +1 -0
  105. writer/static/assets/BuilderSidebarComponentTree-DLltgas5.js +1 -0
  106. writer/static/assets/BuilderSidebarComponentTree-DYu1F793.css +1 -0
  107. writer/static/assets/BuilderSidebarToolkit-CApZNTAq.js +7 -0
  108. writer/static/assets/BuilderSidebarToolkit-CwqbjRv8.css +1 -0
  109. writer/static/assets/BuilderTemplateEditor-CYSDeWgV.css +1 -0
  110. writer/static/assets/BuilderTemplateEditor-DnRDRcA0.js +87 -0
  111. writer/static/assets/BuilderVault-2vGoV0sx.js +1 -0
  112. writer/static/assets/BuilderVault-Cx6oQSES.css +1 -0
  113. writer/static/assets/ComponentRenderer-72hqvEvI.css +1 -0
  114. writer/static/assets/ComponentRenderer-D4Pj1i3s.js +1 -0
  115. writer/static/assets/SharedCopyClipboardButton-BipJKGtz.css +1 -0
  116. writer/static/assets/SharedCopyClipboardButton-DNI9kLe6.js +1 -0
  117. writer/static/assets/WdsCheckbox-DKvpPA4D.css +1 -0
  118. writer/static/assets/WdsCheckbox-edQcn1cf.js +1 -0
  119. writer/static/assets/WdsDropdownMenu-CzzPN9Wg.css +1 -0
  120. writer/static/assets/WdsDropdownMenu-DQnrRBNV.js +1 -0
  121. writer/static/assets/WdsFieldWrapper-Cmufx5Nj.js +1 -0
  122. writer/static/assets/WdsFieldWrapper-CsemOh8D.css +1 -0
  123. writer/static/assets/WdsTabs-DKj7BqI0.css +1 -0
  124. writer/static/assets/WdsTabs-DcfY_zn5.js +1 -0
  125. writer/static/assets/abap-D8nrxEjS.js +6 -0
  126. writer/static/assets/apex-BrXDlLUW.js +6 -0
  127. writer/static/assets/art-paper-D70v1WMA.svg +180 -0
  128. writer/static/assets/azcli-CElzELwZ.js +6 -0
  129. writer/static/assets/bat-CUsyEhik.js +6 -0
  130. writer/static/assets/bicep-BtxyJn6H.js +7 -0
  131. writer/static/assets/cameligo-ClBCoF8h.js +6 -0
  132. writer/static/assets/clojure-B9TqLHAk.js +6 -0
  133. writer/static/assets/codicon-BA2IlpFX.ttf +0 -0
  134. writer/static/assets/coffee-DYsfeylR.js +6 -0
  135. writer/static/assets/cpp-VVGvvgir.js +6 -0
  136. writer/static/assets/csharp-Z6z2stHy.js +6 -0
  137. writer/static/assets/csp-DgZoLDI1.js +6 -0
  138. writer/static/assets/css-KqQ96-gC.js +8 -0
  139. writer/static/assets/css.worker-DvNUQFd1.js +84 -0
  140. writer/static/assets/cssMode-BYq4oZGq.js +9 -0
  141. writer/static/assets/cypher-CYoSlgTu.js +6 -0
  142. writer/static/assets/dart-BGDl7St1.js +6 -0
  143. writer/static/assets/dockerfile-CuCtxA7T.js +6 -0
  144. writer/static/assets/ecl-BCTFAUpS.js +6 -0
  145. writer/static/assets/editor.worker-BVwmgLrR.js +11 -0
  146. writer/static/assets/elixir-C7hRTYZ9.js +6 -0
  147. writer/static/assets/flow9-Bi_qi707.js +6 -0
  148. writer/static/assets/freemarker2-CnNourkO.js +8 -0
  149. writer/static/assets/fsharp-CxaaEKKi.js +6 -0
  150. writer/static/assets/go-DUImKuGY.js +6 -0
  151. writer/static/assets/graphql-D5sGVkLV.js +6 -0
  152. writer/static/assets/handlebars-Bm22yapJ.js +6 -0
  153. writer/static/assets/hcl-zD_CCkZ1.js +6 -0
  154. writer/static/assets/html-CAKAfoZF.js +6 -0
  155. writer/static/assets/html.worker-BJMlcbMU.js +458 -0
  156. writer/static/assets/htmlMode-BGZ97n-V.js +9 -0
  157. writer/static/assets/index-5u5REPT4.js +16 -0
  158. writer/static/assets/index-BKNuk68o.css +1 -0
  159. writer/static/assets/index-BQNXU3IR.js +17 -0
  160. writer/static/assets/index-BQr1pfrb.js +1 -0
  161. writer/static/assets/index-DHXAd5Yn.js +4 -0
  162. writer/static/assets/index-Zki-pfO-.js +8525 -0
  163. writer/static/assets/index.esm-B1ZQtduY.js +17 -0
  164. writer/static/assets/ini-8kKHd4ZL.js +6 -0
  165. writer/static/assets/java-De1axCfe.js +6 -0
  166. writer/static/assets/javascript-X1f02eyK.js +6 -0
  167. writer/static/assets/json.worker-BwvX8PuZ.js +42 -0
  168. writer/static/assets/jsonMode-hT0bNgT8.js +11 -0
  169. writer/static/assets/julia-D3ApGBxz.js +6 -0
  170. writer/static/assets/kotlin-GbSrCElU.js +6 -0
  171. writer/static/assets/less-DNUaDNdz.js +7 -0
  172. writer/static/assets/lexon-Bg9QKxBu.js +6 -0
  173. writer/static/assets/liquid-KmCCiJw2.js +6 -0
  174. writer/static/assets/lua-Crkvc3mc.js +6 -0
  175. writer/static/assets/m3-DsrzVyM1.js +6 -0
  176. writer/static/assets/mapbox-gl-C0cyFYYW.js +2329 -0
  177. writer/static/assets/markdown-CY5IOZuu.js +6 -0
  178. writer/static/assets/marked.esm-273vDTCT.js +45 -0
  179. writer/static/assets/mdx-DtRFauUw.js +6 -0
  180. writer/static/assets/mips-BE8RsGBA.js +6 -0
  181. writer/static/assets/msdax-N5ajIiFQ.js +6 -0
  182. writer/static/assets/mysql-DRxbB97D.js +6 -0
  183. writer/static/assets/objective-c-BHUZy23s.js +6 -0
  184. writer/static/assets/pascal-BemVzBTY.js +6 -0
  185. writer/static/assets/pascaligo-BACCcnx_.js +6 -0
  186. writer/static/assets/pdf-B6-yWJ-Y.js +12 -0
  187. writer/static/assets/pdf.worker.min-CyUfim15.mjs +21 -0
  188. writer/static/assets/perl-CuU66Ptk.js +6 -0
  189. writer/static/assets/pgsql-CQ6TMH2r.js +6 -0
  190. writer/static/assets/php-BvyzZa65.js +6 -0
  191. writer/static/assets/pla-DrIuu9u1.js +6 -0
  192. writer/static/assets/plotly.min-DutuuatZ.js +4030 -0
  193. writer/static/assets/poppins-latin-300-italic-4WBEAciR.woff +0 -0
  194. writer/static/assets/poppins-latin-300-italic-EWCPeN2Y.woff2 +0 -0
  195. writer/static/assets/poppins-latin-300-normal-DCNuMXUj.woff +0 -0
  196. writer/static/assets/poppins-latin-300-normal-Dku2WoCh.woff2 +0 -0
  197. writer/static/assets/poppins-latin-400-italic-B4GYq972.woff2 +0 -0
  198. writer/static/assets/poppins-latin-400-italic-BPejoDS-.woff +0 -0
  199. writer/static/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
  200. writer/static/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  201. writer/static/assets/poppins-latin-500-italic-Ce_qjtl5.woff +0 -0
  202. writer/static/assets/poppins-latin-500-italic-o28Otv0U.woff2 +0 -0
  203. writer/static/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  204. writer/static/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
  205. writer/static/assets/poppins-latin-600-italic-BhOZippK.woff +0 -0
  206. writer/static/assets/poppins-latin-600-italic-CZ4wqKBi.woff2 +0 -0
  207. writer/static/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
  208. writer/static/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  209. writer/static/assets/poppins-latin-700-italic-CW91C-LJ.woff +0 -0
  210. writer/static/assets/poppins-latin-700-italic-RKf6esGj.woff2 +0 -0
  211. writer/static/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
  212. writer/static/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
  213. writer/static/assets/poppins-latin-ext-300-italic-CBzyU4Pf.woff +0 -0
  214. writer/static/assets/poppins-latin-ext-300-italic-DdDvTq5-.woff2 +0 -0
  215. writer/static/assets/poppins-latin-ext-300-normal-7Zg2msWE.woff2 +0 -0
  216. writer/static/assets/poppins-latin-ext-300-normal-C9p7gvmA.woff +0 -0
  217. writer/static/assets/poppins-latin-ext-400-italic-BiCGV3eO.woff2 +0 -0
  218. writer/static/assets/poppins-latin-ext-400-italic-gsPYOGqV.woff +0 -0
  219. writer/static/assets/poppins-latin-ext-400-normal-CIpeJEZw.woff2 +0 -0
  220. writer/static/assets/poppins-latin-ext-400-normal-Ce_uWq1Z.woff +0 -0
  221. writer/static/assets/poppins-latin-ext-500-italic-CwrTHwbn.woff2 +0 -0
  222. writer/static/assets/poppins-latin-ext-500-italic-jdc8Bv4M.woff +0 -0
  223. writer/static/assets/poppins-latin-ext-500-normal-Bl1-S02S.woff +0 -0
  224. writer/static/assets/poppins-latin-ext-500-normal-H4Q0z8D2.woff2 +0 -0
  225. writer/static/assets/poppins-latin-ext-600-italic-BqeDa496.woff2 +0 -0
  226. writer/static/assets/poppins-latin-ext-600-italic-C7MQPb_A.woff +0 -0
  227. writer/static/assets/poppins-latin-ext-600-normal-Cn4C8475.woff2 +0 -0
  228. writer/static/assets/poppins-latin-ext-600-normal-DB6FJURc.woff +0 -0
  229. writer/static/assets/poppins-latin-ext-700-italic-BAdhB_WS.woff2 +0 -0
  230. writer/static/assets/poppins-latin-ext-700-italic-WKTwQMp8.woff +0 -0
  231. writer/static/assets/poppins-latin-ext-700-normal-CE2WFKmF.woff +0 -0
  232. writer/static/assets/poppins-latin-ext-700-normal-DDaViAzG.woff2 +0 -0
  233. writer/static/assets/postiats-BR_hrfni.js +6 -0
  234. writer/static/assets/powerquery-CKDUeRmd.js +6 -0
  235. writer/static/assets/powershell-Dsa4rhA_.js +6 -0
  236. writer/static/assets/protobuf-CGsvhooB.js +7 -0
  237. writer/static/assets/pug-D2p3uOX2.js +6 -0
  238. writer/static/assets/python-DVhxg746.js +6 -0
  239. writer/static/assets/qsharp-B7F3HtPF.js +6 -0
  240. writer/static/assets/r-3aLoi2fs.js +6 -0
  241. writer/static/assets/razor-DR5Ns_BC.js +6 -0
  242. writer/static/assets/redis-jqFeRM5s.js +6 -0
  243. writer/static/assets/redshift-BriwQgXR.js +6 -0
  244. writer/static/assets/restructuredtext-hbBFZ0w9.js +6 -0
  245. writer/static/assets/ruby-ByThyB2Q.js +6 -0
  246. writer/static/assets/rust-DIEZMp5R.js +6 -0
  247. writer/static/assets/sb-C6Gjjw_x.js +6 -0
  248. writer/static/assets/scala-DZNw3jJB.js +6 -0
  249. writer/static/assets/scheme-55eqh71t.js +6 -0
  250. writer/static/assets/scss-D-OVkc4F.js +8 -0
  251. writer/static/assets/serialization-DJC7NP0N.js +20 -0
  252. writer/static/assets/shell-DSpi8_qN.js +6 -0
  253. writer/static/assets/solidity-BHddiNFS.js +6 -0
  254. writer/static/assets/sophia-D6taVZFb.js +6 -0
  255. writer/static/assets/sparql-LA0C7mUc.js +6 -0
  256. writer/static/assets/sql-C3-3IcFM.js +6 -0
  257. writer/static/assets/st-C4g7059C.js +6 -0
  258. writer/static/assets/swift-DNI1vH3h.js +8 -0
  259. writer/static/assets/systemverilog-DL_FVbcQ.js +6 -0
  260. writer/static/assets/tcl-DVJXmIwd.js +6 -0
  261. writer/static/assets/ts.worker-CwG1rUES.js +37021 -0
  262. writer/static/assets/tsMode-BNUEZzir.js +16 -0
  263. writer/static/assets/twig-BVWDLtw5.js +6 -0
  264. writer/static/assets/typescript-CRVt7Hx0.js +6 -0
  265. writer/static/assets/useBlueprintRun-C00bCxh-.js +1 -0
  266. writer/static/assets/useKeyValueEditor-nDmI7cTJ.js +1 -0
  267. writer/static/assets/useListResources-DLkZhRSJ.js +1 -0
  268. writer/static/assets/vb-Btz91-7U.js +6 -0
  269. writer/static/assets/vega-embed.module-SNP5iNdJ.js +201 -0
  270. writer/static/assets/wgsl-D8V_buCG.js +303 -0
  271. writer/static/assets/xml-C_6-t1tb.js +6 -0
  272. writer/static/assets/yaml-DIw8G7jk.js +6 -0
  273. writer/static/components/annotatedtext.svg +4 -0
  274. writer/static/components/avatar.svg +4 -0
  275. writer/static/components/blueprints_addtostatelist.svg +4 -0
  276. writer/static/components/blueprints_apitrigger.svg +4 -0
  277. writer/static/components/blueprints_calleventhandler.svg +9 -0
  278. writer/static/components/blueprints_category_Logic.svg +4 -0
  279. writer/static/components/blueprints_category_Other.svg +4 -0
  280. writer/static/components/blueprints_category_Triggers.svg +4 -0
  281. writer/static/components/blueprints_category_Writer.svg +25 -0
  282. writer/static/components/blueprints_code.svg +9 -0
  283. writer/static/components/blueprints_crontrigger.svg +6 -0
  284. writer/static/components/blueprints_foreach.svg +4 -0
  285. writer/static/components/blueprints_httprequest.svg +11 -0
  286. writer/static/components/blueprints_logmessage.svg +11 -0
  287. writer/static/components/blueprints_parsejson.svg +4 -0
  288. writer/static/components/blueprints_returnvalue.svg +4 -0
  289. writer/static/components/blueprints_runblueprint.svg +4 -0
  290. writer/static/components/blueprints_setstate.svg +4 -0
  291. writer/static/components/blueprints_uieventtrigger.svg +4 -0
  292. writer/static/components/blueprints_writeraddchatmessage.svg +19 -0
  293. writer/static/components/blueprints_writeraddtokg.svg +19 -0
  294. writer/static/components/blueprints_writerchat.svg +11 -0
  295. writer/static/components/blueprints_writerchatreply.svg +19 -0
  296. writer/static/components/blueprints_writerclassification.svg +24 -0
  297. writer/static/components/blueprints_writercompletion.svg +14 -0
  298. writer/static/components/blueprints_writerinitchat.svg +11 -0
  299. writer/static/components/blueprints_writernocodeapp.svg +14 -0
  300. writer/static/components/button.svg +4 -0
  301. writer/static/components/category_Content.svg +4 -0
  302. writer/static/components/category_Embed.svg +4 -0
  303. writer/static/components/category_Input.svg +5 -0
  304. writer/static/components/category_Layout.svg +9 -0
  305. writer/static/components/category_Other.svg +4 -0
  306. writer/static/components/chatbot.svg +4 -0
  307. writer/static/components/checkboxinput.svg +4 -0
  308. writer/static/components/colorinput.svg +11 -0
  309. writer/static/components/column.svg +4 -0
  310. writer/static/components/columns.svg +4 -0
  311. writer/static/components/dataframe.svg +4 -0
  312. writer/static/components/dateinput.svg +4 -0
  313. writer/static/components/dropdowninput.svg +5 -0
  314. writer/static/components/fileinput.svg +4 -0
  315. writer/static/components/googlemaps.svg +4 -0
  316. writer/static/components/header.svg +4 -0
  317. writer/static/components/heading.svg +9 -0
  318. writer/static/components/horizontalstack.svg +4 -0
  319. writer/static/components/html.svg +9 -0
  320. writer/static/components/icon.svg +4 -0
  321. writer/static/components/iframe.svg +4 -0
  322. writer/static/components/image.svg +11 -0
  323. writer/static/components/jsonviewer.svg +4 -0
  324. writer/static/components/link.svg +12 -0
  325. writer/static/components/mapbox.svg +4 -0
  326. writer/static/components/message.svg +4 -0
  327. writer/static/components/metric.svg +4 -0
  328. writer/static/components/multiselectinput.svg +4 -0
  329. writer/static/components/numberinput.svg +4 -0
  330. writer/static/components/page.svg +50 -0
  331. writer/static/components/pagination.svg +4 -0
  332. writer/static/components/pdf.svg +4 -0
  333. writer/static/components/plotlygraph.svg +7 -0
  334. writer/static/components/progressbar.svg +5 -0
  335. writer/static/components/radioinput.svg +4 -0
  336. writer/static/components/rangeinput.svg +4 -0
  337. writer/static/components/ratinginput.svg +4 -0
  338. writer/static/components/repeater.svg +4 -0
  339. writer/static/components/reuse.svg +4 -0
  340. writer/static/components/section.svg +4 -0
  341. writer/static/components/selectinput.svg +5 -0
  342. writer/static/components/separator.svg +4 -0
  343. writer/static/components/sidebar.svg +4 -0
  344. writer/static/components/sliderinput.svg +4 -0
  345. writer/static/components/step.svg +4 -0
  346. writer/static/components/steps.svg +4 -0
  347. writer/static/components/switchinput.svg +4 -0
  348. writer/static/components/tab.svg +4 -0
  349. writer/static/components/tabs.svg +4 -0
  350. writer/static/components/tags.svg +11 -0
  351. writer/static/components/text.svg +4 -0
  352. writer/static/components/textareainput.svg +11 -0
  353. writer/static/components/textinput.svg +4 -0
  354. writer/static/components/timeinput.svg +4 -0
  355. writer/static/components/timer.svg +4 -0
  356. writer/static/components/vegalitechart.svg +7 -0
  357. writer/static/components/videoplayer.svg +11 -0
  358. writer/static/components/webcamcapture.svg +4 -0
  359. writer/static/favicon.png +0 -0
  360. writer/static/index.html +84 -0
  361. writer/static/status/cancelled.svg +5 -0
  362. writer/static/status/error.svg +5 -0
  363. writer/static/status/skipped.svg +4 -0
  364. writer/static/status/stopped.svg +4 -0
  365. writer/static/status/success.svg +4 -0
  366. writer/sync.py +431 -0
  367. writer/ui.py +2268 -0
  368. writer/vault.py +48 -0
  369. writer/wf_project.py +90 -66
  370. writer-1.25.1rc1.dist-info/METADATA +92 -0
  371. writer-1.25.1rc1.dist-info/RECORD +382 -0
  372. {writer-0.8.3rc4.dist-info → writer-1.25.1rc1.dist-info}/WHEEL +1 -1
  373. writer/blocks/runworkflow.py +0 -59
  374. writer/workflows.py +0 -183
  375. writer-0.8.3rc4.dist-info/METADATA +0 -117
  376. writer-0.8.3rc4.dist-info/RECORD +0 -44
  377. {writer-0.8.3rc4.dist-info → writer-1.25.1rc1.dist-info}/entry_points.txt +0 -0
  378. {writer-0.8.3rc4.dist-info → writer-1.25.1rc1.dist-info/licenses}/LICENSE.txt +0 -0
writer/blueprints.py ADDED
@@ -0,0 +1,839 @@
1
+ import hashlib
2
+ import itertools
3
+ import json
4
+ import logging
5
+ import os
6
+ import threading
7
+ import time
8
+ from concurrent.futures import FIRST_COMPLETED, Future, ThreadPoolExecutor, wait
9
+ from contextlib import contextmanager
10
+ from contextvars import ContextVar, copy_context
11
+ from typing import Any, Dict, Generator, List, Literal, Optional, Union
12
+
13
+ import writer.blocks
14
+ import writer.blocks.base_block
15
+ import writer.core
16
+ import writer.core_ui
17
+ from writer.journal import use_journal_record_context
18
+ from writer.ss_types import BlueprintExecutionError, BlueprintExecutionLog, WriterConfigurationError
19
+
20
+ MAX_DAG_DEPTH = 32
21
+ MAX_LOG_ITERABLE_SIZE = 100
22
+ MAX_LOG_STRING_LENGTH = 5000
23
+
24
+ _current_block: ContextVar[Optional[writer.blocks.base_block.BlueprintBlock]] = \
25
+ ContextVar("current_block", default=None)
26
+
27
+
28
+ class BlueprintRunManager:
29
+ def __init__(self):
30
+ self._runs: Dict[str, Dict] = {}
31
+ self._lock = threading.Lock()
32
+
33
+ @contextmanager
34
+ def register(self, run_id: str):
35
+ event = self.register_run(run_id)
36
+ try:
37
+ yield event
38
+ finally:
39
+ self.deregister_run(run_id)
40
+
41
+ def register_run(self, run_id: str):
42
+ with self._lock:
43
+ if run_id not in self._runs:
44
+ self._runs[run_id] = {"counter": 0, "event": threading.Event()}
45
+ self._runs[run_id]["counter"] += 1
46
+ event = self._runs[run_id]["event"]
47
+ return event
48
+
49
+ def deregister_run(self, run_id: str):
50
+ with self._lock:
51
+ if run_id in self._runs:
52
+ self._runs[run_id]["counter"] -= 1
53
+ if self._runs[run_id]["counter"] <= 0:
54
+ del self._runs[run_id]
55
+
56
+ def cancel_run(self, run_id: str):
57
+ with self._lock:
58
+ if run_id in self._runs:
59
+ self._runs[run_id]["event"].set()
60
+
61
+
62
+ class BlueprintRunner:
63
+ def __init__(self, session: writer.core.WriterSession):
64
+ self.session = session
65
+ self.executor_lock = threading.Lock()
66
+ self.run_manager = BlueprintRunManager()
67
+
68
+ @property
69
+ def api_blueprints(self):
70
+ return self._gather_api_blueprints()
71
+
72
+ @contextmanager
73
+ def _get_executor(self) -> Generator[ThreadPoolExecutor, None, None]:
74
+ """Return the application's thread pool executor.
75
+
76
+ In normal operation we reuse the main executor provided by the running
77
+ application process. In situations where that process is unavailable
78
+ (for example during tests) a temporary executor is created.
79
+ """
80
+
81
+ new_executor = None
82
+ try:
83
+ try:
84
+ current_app_process = writer.core.get_app_process()
85
+ executor = current_app_process.executor
86
+ except RuntimeError:
87
+ logging.info(
88
+ "The main pool executor isn't being reused. This is only expected in test or debugging situations."
89
+ )
90
+ new_executor = ThreadPoolExecutor(20) # New executor for debugging/testing
91
+ executor = new_executor
92
+
93
+ if not executor:
94
+ raise RuntimeError(
95
+ "The main pool executor isn't available. This is only expected in test or debugging situations."
96
+ )
97
+ yield executor
98
+ finally:
99
+ if new_executor:
100
+ new_executor.shutdown()
101
+
102
+ def execute_ui_trigger(
103
+ self, ref_component_id: str, ref_event_type: str, execution_environment: Dict = {}
104
+ ):
105
+ components = self.session.session_component_tree.get_descendents("blueprints_root")
106
+ ui_triggers = list(filter(lambda c: c.type == "blueprints_uieventtrigger", components))
107
+ for trigger in ui_triggers:
108
+ if trigger.content.get("refComponentId") != ref_component_id:
109
+ continue
110
+ if trigger.content.get("refEventType") != ref_event_type:
111
+ continue
112
+ self.run_branch(trigger.id, None, execution_environment, "UI trigger execution")
113
+
114
+ def run_blueprint_by_key(self, blueprint_key: str, execution_environment: Dict = {}):
115
+ all_components = self.session.session_component_tree.components.values()
116
+ blueprints = list(
117
+ filter(
118
+ lambda c: c.type == "blueprints_blueprint" and c.content.get("key") == blueprint_key,
119
+ all_components,
120
+ )
121
+ )
122
+ if len(blueprints) == 0:
123
+ raise ValueError(f'Blueprint with key "{blueprint_key}" not found.')
124
+ blueprint = blueprints[0]
125
+ return self.run_blueprint(
126
+ blueprint.id, execution_environment, f"Blueprint execution ({blueprint_key})"
127
+ )
128
+
129
+ def is_blueprint_api_available(
130
+ self, blueprint_id: str
131
+ ):
132
+ """
133
+ Checks if a blueprint with the given key is available for API execution.
134
+
135
+ :param blueprint_id: The blueprint identifier.
136
+ :return: True if the blueprint is available for API execution, False otherwise.
137
+ """
138
+ return blueprint_id in self.api_blueprints
139
+
140
+ def get_blueprint_api_trigger(
141
+ self, blueprint_id: str
142
+ ):
143
+ """
144
+ Retrieves the API trigger for a given blueprint key.
145
+
146
+ :param blueprint_key: The blueprint identifier.
147
+ :return: The API trigger component.
148
+ """
149
+ if not self.is_blueprint_api_available(blueprint_id):
150
+ raise ValueError(
151
+ f'API trigger not found for blueprint "{blueprint_id}".'
152
+ )
153
+ return self.api_blueprints[blueprint_id]
154
+
155
+ def _gather_api_blueprints(self):
156
+ """
157
+ Gathers all blueprints that have an API trigger.
158
+
159
+ :return: A set of blueprint keys that have an API trigger.
160
+ """
161
+ triggers = [
162
+ c for c in self.session.session_component_tree.components.values()
163
+ if c.type == "blueprints_apitrigger"
164
+ ]
165
+ api_blueprints = {}
166
+
167
+ for trigger in triggers:
168
+ parent_blueprint_id = \
169
+ self.session.session_component_tree.get_parent(trigger.id)[0]
170
+ parent_blueprint = \
171
+ self.session.session_component_tree.get_component(
172
+ parent_blueprint_id
173
+ )
174
+
175
+ if (
176
+ parent_blueprint
177
+ and
178
+ parent_blueprint.type == "blueprints_blueprint"
179
+ ):
180
+ # Store the blueprint key against its trigger ID
181
+ api_blueprints[parent_blueprint_id] = \
182
+ trigger.id
183
+
184
+ return api_blueprints
185
+
186
+ def run_blueprint_via_api(
187
+ self,
188
+ blueprint_id: str,
189
+ trigger_type: Literal["API", "Cron"],
190
+ branch_id: Optional[str] = None,
191
+ execution_environment: Optional[Dict[str, Any]] = None
192
+ ):
193
+ """
194
+ Executes a blueprint by its key via the API.
195
+
196
+ :param blueprint_id: The blueprint identifier.
197
+ :param execution_environment: The execution environment for
198
+ the blueprint.
199
+ :return: The result of the blueprint execution.
200
+ """
201
+ if execution_environment is None:
202
+ execution_environment = {}
203
+
204
+ trigger_id = branch_id
205
+ if trigger_id is None:
206
+ trigger_id = self.get_blueprint_api_trigger(blueprint_id)
207
+
208
+ return self.run_branch(
209
+ trigger_id,
210
+ None,
211
+ execution_environment,
212
+ f"{trigger_type} trigger execution ({blueprint_id} -> {trigger_id})"
213
+ )
214
+
215
+ def run_blueprint_batch(self, blueprint_key: str, execution_environments: List[Dict]):
216
+ """
217
+ Executes the same blueprint multiple times sequentially with different execution environments.
218
+
219
+ :param blueprint_key: The blueprint identifier (same blueprint for all executions).
220
+ :param execution_environments: A list of execution environments, one per execution.
221
+ :return: A list of results in the same order as execution_environments.
222
+ """
223
+ results = []
224
+ for env in execution_environments:
225
+ result = self.run_blueprint_by_key(blueprint_key, env)
226
+ results.append(result)
227
+
228
+ return results
229
+
230
+ def _get_blueprint_nodes(self, component_id):
231
+ current_node_id = component_id
232
+ while current_node_id is not None:
233
+ node = self.session.session_component_tree.get_component(current_node_id)
234
+ if not node:
235
+ break
236
+ if node.type == "blueprints_blueprint":
237
+ nodes = self.session.session_component_tree.get_descendents(current_node_id)
238
+ return [node for node in nodes if node.type != 'note']
239
+ current_node_id = node.parentId
240
+ return []
241
+
242
+ def run_branch(
243
+ self,
244
+ start_node_id: str,
245
+ branch_out_id: Optional[str],
246
+ execution_environment: Dict,
247
+ title: str = "Branch execution",
248
+ ):
249
+ builder = GraphBuilder(
250
+ components=self._get_blueprint_nodes(start_node_id),
251
+ tools=writer.blocks.base_block.block_map
252
+ )
253
+ if branch_out_id is None:
254
+ builder.set_start_node(start_node_id)
255
+ else:
256
+ builder.set_start_edge(start_node_id, branch_out_id)
257
+
258
+ return GraphRunner(
259
+ builder.build(),
260
+ execution_environment, self, title=title
261
+ ).run()
262
+
263
+ def run_branch_batch(
264
+ self, base_component_id: str, base_outcome: str, execution_environments: List[Dict]
265
+ ):
266
+ """
267
+ Executes the same branch multiple times sequentially with different execution environments.
268
+ """
269
+ results = []
270
+ for env in execution_environments:
271
+ result = self.run_branch(base_component_id, base_outcome, env)
272
+ results.append(result)
273
+
274
+ return results
275
+
276
+ def run_blueprint(
277
+ self, component_id: str, execution_environment: Dict, title="Blueprint execution"
278
+ ):
279
+ builder = GraphBuilder(
280
+ components=self._get_blueprint_nodes(component_id),
281
+ tools=writer.blocks.base_block.block_map
282
+ )
283
+
284
+ return GraphRunner(
285
+ builder.build(),
286
+ execution_environment, self, title=title
287
+ ).run()
288
+
289
+ def cancel_blueprint_execution(self, run_id: str):
290
+ self.run_manager.cancel_run(run_id)
291
+
292
+
293
+ class GraphNode:
294
+ tool_class: writer.blocks.base_block.BlueprintBlock_T
295
+ component: writer.core_ui.Component
296
+ future: Optional[Future] = None
297
+ tool: Optional[writer.blocks.base_block.BlueprintBlock] = None
298
+ # filrered lists of inputs and outputs with only edges from graph
299
+ inputs: List[Any]
300
+ outputs: List[Any]
301
+ status: Optional[str] = None
302
+ _message: Optional[str] = None
303
+
304
+ def __init__(self, component: writer.core_ui.Component, graph: "Graph"):
305
+ self.component = component
306
+ self.graph = graph
307
+ tool_class = graph.tools.get(component.type)
308
+ self.inputs = []
309
+ self.outputs = []
310
+ if not tool_class:
311
+ raise WriterConfigurationError(
312
+ f"Component type '{component.type}' is not registered as a block."
313
+ )
314
+ self.tool_class = tool_class
315
+
316
+
317
+ @property
318
+ def id(self) -> str:
319
+ return self.component.id
320
+
321
+ @property
322
+ def result(self) -> Optional[Union[str, Dict]]:
323
+ if self.tool:
324
+ return self.tool.result
325
+ return None
326
+
327
+ @property
328
+ def outcome(self) -> Optional[str]:
329
+ if self.status:
330
+ return self.status
331
+ if self.tool:
332
+ return self.tool.outcome
333
+ return None
334
+
335
+ @property
336
+ def message(self) -> Optional[str]:
337
+ if self._message:
338
+ return self._message
339
+ if self.tool:
340
+ return self.tool.message
341
+ return None
342
+
343
+ @message.setter
344
+ def message(self, value: str):
345
+ self._message = value
346
+
347
+ @property
348
+ def return_value(self) -> Optional[Any]:
349
+ if self.tool:
350
+ return self.tool.return_value
351
+ return None
352
+
353
+ def run_tool(self, tool: writer.blocks.base_block.BlueprintBlock) -> "GraphNode":
354
+ start_time = time.time()
355
+ tool.started_at = start_time
356
+
357
+ call_stack = tool.execution_environment.get("call_stack", []) + [self.id]
358
+ call_depth = call_stack.count(tool.component.id)
359
+ if call_depth > MAX_DAG_DEPTH:
360
+ error_message = f"Maximum call depth ({MAX_DAG_DEPTH}) exceeded. Check that you don't have any unintended circular references."
361
+ tool.outcome = "error"
362
+ tool.message = error_message
363
+ raise RuntimeError(error_message)
364
+ tool.execution_environment["call_stack"] = call_stack
365
+ tool.execution_environment["trace"] = []
366
+
367
+ try:
368
+ tool.outcome = "in_progress"
369
+ with use_current_block(tool):
370
+ tool.run()
371
+ if self.outcome == "stopped":
372
+ return self
373
+ tool.outcome = tool.outcome or "success"
374
+ except BlueprintExecutionError as e:
375
+ raise e
376
+ except BaseException as e:
377
+ if not tool.outcome or tool.outcome == "in_progress":
378
+ tool.outcome = "error"
379
+ if isinstance(e, WriterConfigurationError):
380
+ tool.message = str(e)
381
+ else:
382
+ tool.message = repr(e)
383
+ if self._is_error_handled(tool.component, tool.outcome):
384
+ return self
385
+ else:
386
+ raise e
387
+ finally:
388
+ tool.execution_time_in_seconds = time.time() - start_time
389
+ try:
390
+ tool.execution_environment_snapshot = {
391
+ k: v for k, v in tool.execution_environment.items() if k != "vault"
392
+ }
393
+ except Exception:
394
+ # pragma: no cover - best effort defensive code
395
+ logging.debug(
396
+ "Couldn't snapshot execution environment", exc_info=True
397
+ )
398
+
399
+ return self
400
+
401
+ def _is_error_handled(self, component: writer.core_ui.Component, outcome: str) -> bool:
402
+ if not component.outs:
403
+ return False
404
+ for output in component.outs:
405
+ if output.get("outId") == outcome:
406
+ return True
407
+ return False
408
+
409
+ def _get_env(self, execution_environment: Dict) -> Dict:
410
+ env = execution_environment.copy()
411
+ for inputs in self.inputs:
412
+ from_node = self.graph.get_node(inputs["fromNodeId"])
413
+ if from_node and from_node.tool:
414
+ out_id = inputs.get("outId")
415
+ if out_id and from_node.outcome == out_id:
416
+ result = from_node.result
417
+ env['call_stack'] = from_node.tool.execution_environment.get('call_stack', [])
418
+ env['result'] = result
419
+ env['message'] = from_node.tool.message
420
+
421
+ # Pass through accumulated API calls from previous block (like call_stack)
422
+ env['api_calls'] = from_node.tool.execution_environment.get('api_calls', [])
423
+ env['httpx_requests'] = from_node.tool.execution_environment.get('httpx_requests', [])
424
+ env['results'] = self.graph.get_results()
425
+ return env
426
+
427
+ def can_run(self) -> bool:
428
+ if not self.inputs:
429
+ return True
430
+ # all inputs must be evaluated
431
+ for input in self.inputs:
432
+ from_node = self.graph.get_node(input["fromNodeId"])
433
+ if not from_node or from_node.outcome is None or from_node.outcome == "in_progress":
434
+ return False
435
+ return True
436
+
437
+ def _is_skipped(self) -> bool:
438
+ if not self.inputs:
439
+ return False
440
+ for input in self.inputs:
441
+ from_node = self.graph.get_node(input["fromNodeId"])
442
+ if from_node and from_node.outcome == input.get("outId"):
443
+ return False
444
+ return True
445
+
446
+ def run(self, execution_environment: Dict, runner, executor) -> Future:
447
+ if self.outcome is not None or self._is_skipped():
448
+ self.status = "skipped"
449
+ future: Future = Future()
450
+ future.set_result(self)
451
+ return future
452
+
453
+ self.tool = self.tool_class(self.component, runner, self._get_env(execution_environment))
454
+ self.tool.outcome = "in_progress"
455
+ ctx = copy_context()
456
+ self.future = executor.submit(ctx.run, self.run_tool, self.tool)
457
+ if not isinstance(self.future, Future):
458
+ raise WriterConfigurationError(
459
+ f"Unable to run tool {self.tool.component.id} - the executor did not return a Future."
460
+ )
461
+ return self.future
462
+
463
+ def debug_info(self) -> Dict[str, Any]:
464
+ return {
465
+ "id": self.id,
466
+ "type": self.component.type,
467
+ "outputs": self.outputs,
468
+ "inputs": self.inputs,
469
+ "result": self.result,
470
+ "message": self.message if self.tool else None,
471
+ "return_value": self.tool.return_value if self.tool else None,
472
+ "outcome": self.outcome if self.tool else None,
473
+ }
474
+
475
+ class Graph:
476
+ status: Optional[str] = None
477
+ def __init__(self,
478
+ nodes: List[writer.core_ui.Component],
479
+ tools: Dict[str, writer.blocks.base_block.BlueprintBlock_T]
480
+ ):
481
+ self.tools = tools
482
+ self.nodes = [GraphNode(node, self) for node in nodes]
483
+ self.node_map = {node.id: node for node in self.nodes}
484
+ self._calculate_io()
485
+ self.start_nodes = self._find_start_nodes()
486
+
487
+ def get_start_nodes(self) -> List[GraphNode]:
488
+ return self.start_nodes
489
+
490
+ def get_node(self, node_id: str) -> Optional[GraphNode]:
491
+ return self.node_map.get(node_id)
492
+
493
+ def get_results(self) -> Dict[str, Any]:
494
+ results = {}
495
+ for node in self.nodes:
496
+ if node.tool and node.tool.outcome in ["success", "trigger"]:
497
+ results[node.id] = node.result
498
+ return results
499
+
500
+ def _calculate_io(self):
501
+ for node in self.nodes:
502
+ if not node.component.outs:
503
+ continue
504
+ for output in node.component.outs:
505
+ if output.get("toNodeId") in self.node_map:
506
+ node.outputs.append(output)
507
+ target_node = self.node_map[output.get("toNodeId")]
508
+ target_node.inputs.append({
509
+ "fromNodeId": node.id,
510
+ "outId": output.get("outId"),
511
+ })
512
+
513
+ def _find_start_nodes(self) -> List[GraphNode]:
514
+ start_nodes = []
515
+ output_nodes = set()
516
+ for node in self.nodes:
517
+ if node.outputs:
518
+ for output in node.outputs:
519
+ output_nodes.add(output.get("toNodeId"))
520
+
521
+ for node in self.nodes:
522
+ if node.id not in output_nodes:
523
+ start_nodes.append(node)
524
+
525
+ return start_nodes
526
+ def debug_info(self) -> Dict[str, Any]:
527
+ return {
528
+ "nodes": [node.debug_info() for node in self.nodes],
529
+ "start_nodes": [node.id for node in self.start_nodes],
530
+ "tools": list(self.tools.keys()),
531
+ }
532
+
533
+ class GraphBuilder:
534
+ def __init__(self, components: List[writer.core_ui.Component], tools: Dict[str, writer.blocks.base_block.BlueprintBlock_T]):
535
+ self.components = components
536
+ self.tools = tools
537
+ self.start_ids: List[str] = []
538
+
539
+ def set_start_node(self, start_component_id: str):
540
+ self.start_ids.append(start_component_id)
541
+
542
+ def set_start_edge(self, component_id: str, out_id: str):
543
+ for component in self.components:
544
+ if component.id == component_id:
545
+ if not component.outs:
546
+ break
547
+ for out in component.outs:
548
+ if out.get("outId") == out_id:
549
+ self.start_ids.append(out.get("toNodeId"))
550
+
551
+ def validate_graph(self, graph: Graph):
552
+ """Validates the graph for cycles and unreachable nodes and marks them as errors."""
553
+ visited = set()
554
+ stack = set()
555
+ has_cycle = False
556
+
557
+ def visit(node: GraphNode):
558
+ if node.status == "error":
559
+ return
560
+ if node.id in stack:
561
+ node.status = "error"
562
+ node.message = "Circular dependency detected."
563
+ nonlocal has_cycle
564
+ has_cycle = True
565
+ if node.id in visited:
566
+ return
567
+ visited.add(node.id)
568
+ stack.add(node.id)
569
+ for output in node.outputs:
570
+ next_node = graph.get_node(output["toNodeId"])
571
+ if next_node:
572
+ visit(next_node)
573
+ stack.remove(node.id)
574
+
575
+ for node in graph.nodes:
576
+ if node.id not in visited:
577
+ visit(node)
578
+
579
+ for node in graph.nodes:
580
+ if node.id not in visited:
581
+ node.status = "error"
582
+ if has_cycle:
583
+ graph.status = "error"
584
+
585
+ def build(self) -> Graph:
586
+ graph = Graph(self._filter_components(), self.tools)
587
+ self.validate_graph(graph)
588
+ return graph
589
+
590
+ def _filter_components(self) -> List[writer.core_ui.Component]:
591
+ if not self.start_ids:
592
+ return self.components
593
+ component_map = {component.id: component for component in self.components}
594
+ # todo: remove duplicates
595
+ filtered_components = set()
596
+ queue = [component_map[component_id] for component_id in self.start_ids if component_id in component_map]
597
+ while queue:
598
+ component = queue.pop(0)
599
+ filtered_components.add(component.id)
600
+ if component.outs is None:
601
+ continue
602
+ for out in component.outs:
603
+ next_component_id = out["toNodeId"]
604
+ if next_component_id in component_map and next_component_id not in filtered_components:
605
+ queue.append(component_map[next_component_id])
606
+
607
+ return [
608
+ component_map[component_id] for component_id in filtered_components
609
+ if component_id in component_map
610
+ ]
611
+
612
+
613
+ class StatusLogger:
614
+ def __init__(self,
615
+ graph: Graph,
616
+ runner: BlueprintRunner,
617
+ run_id: str,
618
+ title: str = "Blueprint execution"
619
+ ):
620
+ self.runner = runner
621
+ self.graph = graph
622
+ self.title = title
623
+ self.run_id = run_id
624
+ self.log_id = self._generate_run_id()
625
+ self.lock = threading.Lock()
626
+
627
+ def log(
628
+ self,
629
+ msg: str = "",
630
+ entry_type: Literal["info", "error"] = "info",
631
+ exit: Optional[str] = None,
632
+ ):
633
+ if not writer.core.Config.is_mail_enabled_for_log:
634
+ return
635
+ log_id = self.log_id
636
+ exec_log: BlueprintExecutionLog = BlueprintExecutionLog(runId=self.run_id, summary=[], exit=exit)
637
+ for node in self.graph.nodes:
638
+ #print(node.debug_info())
639
+ if node.tool is None:
640
+ if node.outcome is None:
641
+ exec_log.summary.append({"componentId": node.id })
642
+ continue
643
+ else:
644
+ exec_log.summary.append({
645
+ "componentId": node.id,
646
+ "outcome": node.outcome,
647
+ "message": node.message,
648
+ "result": None,
649
+ "returnValue": None,
650
+ "executionEnvironment": {},
651
+ "executionTimeInSeconds": 0,
652
+ })
653
+ continue
654
+ if node.outcome == "stopped":
655
+ exec_log.summary.append(
656
+ {
657
+ "componentId": node.id,
658
+ "outcome": node.outcome,
659
+ "message": node.message,
660
+ "executionTimeInSeconds": node.tool.execution_time_in_seconds,
661
+ }
662
+ )
663
+ continue
664
+ if node.outcome == "in_progress":
665
+ exec_log.summary.append(
666
+ {
667
+ "componentId": node.id,
668
+ "outcome": node.outcome,
669
+ "message": node.message,
670
+ "executionTimeInSeconds": node.tool.execution_time_in_seconds,
671
+ }
672
+ )
673
+ continue
674
+
675
+ exec_log.summary.append(
676
+ {
677
+ "componentId": node.id,
678
+ "outcome": node.outcome,
679
+ "message": node.message,
680
+ "result": self._summarize_data_for_log(node.result),
681
+ "returnValue": self._summarize_data_for_log(node.return_value),
682
+ "executionEnvironment": self._summarize_data_for_log(getattr(node.tool, "execution_environment_snapshot", None)),
683
+ "executionTimeInSeconds": node.tool.execution_time_in_seconds,
684
+ }
685
+ )
686
+ self.runner.session.session_state.add_log_entry(
687
+ entry_type, self.title, msg, blueprint_execution=exec_log, id=log_id
688
+ )
689
+
690
+ def _generate_run_id(self):
691
+ timestamp = str(int(time.time() * 1000))
692
+ salt = os.urandom(8).hex()
693
+ raw_id = f"{self.runner.session.session_id}_{timestamp}_{salt}"
694
+ hashed_id = hashlib.sha256(raw_id.encode()).hexdigest()[:24]
695
+ return hashed_id
696
+
697
+ def _summarize_data_for_log(self, data):
698
+ """Convert arbitrary data into a log friendly representation."""
699
+
700
+ if data is None:
701
+ return None
702
+
703
+ if isinstance(data, list):
704
+ return [self._summarize_data_for_log(item) for item in data[:MAX_LOG_ITERABLE_SIZE]]
705
+ if isinstance(data, dict):
706
+ return {
707
+ k: self._summarize_data_for_log(v)
708
+ for k, v in itertools.islice(data.items(), MAX_LOG_ITERABLE_SIZE)
709
+ }
710
+ if isinstance(data, str):
711
+ if len(data) <= MAX_LOG_STRING_LENGTH:
712
+ return data
713
+ return f"{data[:MAX_LOG_STRING_LENGTH]}... <truncated>"
714
+ if isinstance(data, (int, float, bool, type(None))):
715
+ return data
716
+
717
+ try:
718
+ return json.loads(json.dumps(data))
719
+ except (TypeError, OverflowError):
720
+ return f"Can't be displayed in the log. Value of type: {str(type(data))}."
721
+
722
+ class GraphRunner:
723
+ CANCELATION_CHECK_INTERVAL = 0.1
724
+
725
+ def __init__(self,
726
+ graph: Graph,
727
+ execution_environment: Dict,
728
+ runner,
729
+ title: str = "Blueprint execution"
730
+ ):
731
+ self.runner = runner
732
+ self.graph = graph
733
+ self.execution_environment = execution_environment
734
+ self.run_id = execution_environment.get("blueprint_run_id", self._generate_run_id())
735
+ execution_environment["blueprint_run_id"] = self.run_id
736
+ self.status_logger = StatusLogger(self.graph, self.runner, self.run_id, title)
737
+
738
+ self.queue = self.graph.get_start_nodes()
739
+ self.futures: List[Future[GraphNode]] = []
740
+
741
+ def run(self) -> Optional[Any]:
742
+ if self.graph.status == "error":
743
+ self.status_logger.log("Execution failed due to graph validation errors.", entry_type="error", exit="graph_validation_error")
744
+ return None
745
+ if not self.queue:
746
+ raise WriterConfigurationError("No start nodes found in the blueprint.")
747
+
748
+ with self.runner._get_executor() as executor:
749
+ with self.runner.run_manager.register(self.run_id) as event:
750
+ return self._execute(executor, event)
751
+
752
+ def _execute(self, executor: ThreadPoolExecutor, abort_event: threading.Event) -> Optional[Any]:
753
+ with use_journal_record_context(self.execution_environment, self.status_logger.title, self.graph) as journal_record:
754
+ while self.queue or self.futures:
755
+ while self.queue:
756
+ node: GraphNode = self.queue.pop(0)
757
+ if node.can_run() and node.outcome is None:
758
+ self.futures.append(node.run(self.execution_environment, self.runner, executor))
759
+
760
+ self.status_logger.log("Executing...")
761
+ done, _ = wait(self.futures, timeout=self.CANCELATION_CHECK_INTERVAL, return_when=FIRST_COMPLETED)
762
+ if not done:
763
+ if abort_event.is_set():
764
+ self._cancel_all_jobs()
765
+ self.status_logger.log("Terminated.", entry_type="info", exit="aborted")
766
+ journal_record.set_result("stopped")
767
+ return None
768
+ else:
769
+ continue
770
+ self.status_logger.log("Executing...")
771
+ for future in done:
772
+ if future in self.futures:
773
+ self.futures.remove(future)
774
+ try:
775
+ result_node: GraphNode = future.result()
776
+ except BlueprintExecutionError as e:
777
+ self._cancel_all_jobs()
778
+ self.status_logger.log("Execution failed", entry_type="error", exit=str(e))
779
+ raise e
780
+ except BaseException as e:
781
+ abort_event.set()
782
+ self._cancel_all_jobs()
783
+ self.status_logger.log("Execution failed.", entry_type="error", exit=str(e))
784
+ raise BlueprintExecutionError(
785
+ f"Blueprint execution was stopped due to an error - {e.__class__.__name__}: {e}"
786
+ ) from e
787
+ if result_node.outcome == "stopped":
788
+ continue
789
+ if result_node.return_value is not None:
790
+ self._cancel_local_jobs()
791
+ self.status_logger.log(
792
+ f"Execution completed, node {result_node.id} returned value: {result_node.return_value}",
793
+ entry_type="info",
794
+ exit="return"
795
+ )
796
+
797
+ journal_record.set_result("success")
798
+ return result_node.return_value
799
+ for output in result_node.outputs:
800
+ to_node_id = output.get("toNodeId")
801
+ next_node = self.graph.get_node(to_node_id)
802
+ if next_node:
803
+ self.queue.append(next_node)
804
+
805
+ self.status_logger.log("Execution completed.", entry_type="info", exit="completed")
806
+ journal_record.set_result("success")
807
+ return None
808
+
809
+ def _cancel_local_jobs(self):
810
+ self.queue.clear()
811
+ for node in self.graph.nodes:
812
+ if node.outcome == "in_progress":
813
+ node.status = "stopped"
814
+ self.status_logger.log("Stopped")
815
+ for future in self.futures:
816
+ if not future.done():
817
+ future.cancel()
818
+
819
+ def _cancel_all_jobs(self):
820
+ self._cancel_local_jobs()
821
+ self.runner.cancel_blueprint_execution(self.run_id)
822
+
823
+ def _generate_run_id(self):
824
+ timestamp = str(int(time.time() * 1000))
825
+ salt = os.urandom(8).hex()
826
+ raw_id = f"{self.runner.session.session_id}_{timestamp}_{salt}"
827
+ hashed_id = hashlib.sha256(raw_id.encode()).hexdigest()[:24]
828
+ return hashed_id
829
+
830
+
831
+ def get_current_block() -> Optional[writer.blocks.base_block.BlueprintBlock]:
832
+ return _current_block.get(None)
833
+
834
+
835
+ @contextmanager
836
+ def use_current_block(block: writer.blocks.base_block.BlueprintBlock):
837
+ token = _current_block.set(block)
838
+ yield
839
+ _current_block.reset(token)