writer 0.8.3rc21__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 (265) hide show
  1. writer/ai/__init__.py +277 -28
  2. writer/app_runner.py +236 -32
  3. writer/app_templates/default/.wf/components-blueprints_blueprint-0-0decp3w5erhvl0nw.jsonl +11 -0
  4. writer/app_templates/default/.wf/components-page-0-c0f99a9e-5004-4e75-a6c6-36f17490b134.jsonl +22 -10
  5. writer/app_templates/default/.wf/components-root.jsonl +1 -1
  6. writer/app_templates/default/.wf/components-workflows_root.jsonl +1 -0
  7. writer/app_templates/default/.wf/components-workflows_workflow-0-lfltcky7l1fsm6j2.jsonl +1 -0
  8. writer/app_templates/default/.wf/metadata.json +1 -1
  9. writer/app_templates/default/README.md +3 -0
  10. writer/app_templates/default/main.py +8 -5
  11. writer/app_templates/default/requirements.txt +1 -0
  12. writer/app_templates/default/static/agent_builder_demo.png +0 -0
  13. writer/app_templates/hello/main.py +3 -0
  14. writer/auth.py +7 -2
  15. writer/autogen.py +26 -9
  16. writer/blocks/__init__.py +21 -1
  17. writer/blocks/addtostatelist.py +5 -4
  18. writer/blocks/apitrigger.py +45 -0
  19. writer/blocks/base_block.py +134 -14
  20. writer/blocks/changepage.py +1 -1
  21. writer/blocks/code.py +27 -11
  22. writer/blocks/crontrigger.py +49 -0
  23. writer/blocks/foreach.py +3 -3
  24. writer/blocks/httprequest.py +8 -24
  25. writer/blocks/ifelse.py +71 -0
  26. writer/blocks/logmessage.py +2 -2
  27. writer/blocks/returnvalue.py +2 -2
  28. writer/blocks/runblueprint.py +2 -2
  29. writer/blocks/setstate.py +5 -5
  30. writer/blocks/sharedblueprint.py +86 -0
  31. writer/blocks/writeraddchatmessage.py +45 -7
  32. writer/blocks/writeraddtokg.py +31 -4
  33. writer/blocks/writeraskkg.py +27 -34
  34. writer/blocks/writerchat.py +14 -9
  35. writer/blocks/writerchatreply.py +279 -0
  36. writer/blocks/writerchatreplywithtoolconfig.py +393 -0
  37. writer/blocks/writerclassification.py +42 -5
  38. writer/blocks/writercompletion.py +4 -4
  39. writer/blocks/writerfileapi.py +4 -4
  40. writer/blocks/writerinitchat.py +5 -4
  41. writer/blocks/writerkeyvaluestorage.py +106 -0
  42. writer/blocks/writernocodeapp.py +4 -4
  43. writer/blocks/writerparsepdf.py +8 -6
  44. writer/blocks/writerstructuredoutput.py +105 -0
  45. writer/blocks/writertoolcalling.py +106 -51
  46. writer/blocks/writervision.py +141 -0
  47. writer/blocks/writerwebsearch.py +175 -0
  48. writer/blueprints.py +715 -251
  49. writer/command_line.py +52 -16
  50. writer/core.py +200 -35
  51. writer/core_ui.py +4 -0
  52. writer/evaluator.py +38 -24
  53. writer/journal.py +227 -0
  54. writer/keyvalue_storage.py +93 -0
  55. writer/logs.py +277 -0
  56. writer/serve.py +402 -198
  57. writer/ss_types.py +41 -0
  58. writer/static/assets/BaseMarkdown-Wrvby5J8.js +1 -0
  59. writer/static/assets/BlueprintToolbar-BuXNRxWT.js +1 -0
  60. writer/static/assets/BlueprintToolbar-wpfX0jo_.css +1 -0
  61. writer/static/assets/BuilderApp-PTOI76jZ.js +8 -0
  62. writer/static/assets/BuilderApp-WimUfNZr.css +1 -0
  63. writer/static/assets/BuilderApplicationSelect-DXzy4e_h.js +7 -0
  64. writer/static/assets/BuilderApplicationSelect-XaM1D5fv.css +1 -0
  65. writer/static/assets/BuilderBlueprintLibraryPanel-Ckrhknlh.css +1 -0
  66. writer/static/assets/BuilderBlueprintLibraryPanel-DBDzhTlc.js +1 -0
  67. writer/static/assets/{BuilderEmbeddedCodeEditor-DiDqfWdt.css → BuilderEmbeddedCodeEditor-B0bcjlhk.css} +1 -1
  68. writer/static/assets/{BuilderEmbeddedCodeEditor-CbK-r9w6.js → BuilderEmbeddedCodeEditor-Dn7eDICN.js} +7 -7
  69. writer/static/assets/BuilderGraphSelect-C-LRsO8W.js +7 -0
  70. writer/static/assets/BuilderGraphSelect-D7B61d5s.css +1 -0
  71. writer/static/assets/{BuilderInsertionLabel-CDlWX-mE.js → BuilderInsertionLabel-BhyL9wgn.js} +1 -1
  72. writer/static/assets/{BuilderInsertionOverlay-B7-TsHNC.js → BuilderInsertionOverlay-MkAIVruY.js} +1 -1
  73. writer/static/assets/BuilderJournal-A0LcEwGI.js +7 -0
  74. writer/static/assets/BuilderJournal-DHv3Pvvm.css +1 -0
  75. writer/static/assets/BuilderModelSelect-CdSo_sih.js +7 -0
  76. writer/static/assets/BuilderModelSelect-Dc4IPLp2.css +1 -0
  77. writer/static/assets/BuilderSettings-BDwZBveu.js +16 -0
  78. writer/static/assets/BuilderSettings-lZkOXEYw.css +1 -0
  79. writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-3O6jKBXD.js +4 -0
  80. writer/static/assets/BuilderSettingsArtifactAPITriggerDetails-DnX66iRg.css +1 -0
  81. writer/static/assets/BuilderSettingsDeploySharedBlueprint-BR_3ptsd.js +1 -0
  82. writer/static/assets/BuilderSettingsDeploySharedBlueprint-KJTl8gxP.css +1 -0
  83. writer/static/assets/BuilderSettingsHandlers-CBtEQFSo.js +1 -0
  84. writer/static/assets/BuilderSettingsHandlers-DJPeASfz.css +1 -0
  85. writer/static/assets/BuilderSidebarComponentTree-DLltgas5.js +1 -0
  86. writer/static/assets/BuilderSidebarComponentTree-DYu1F793.css +1 -0
  87. writer/static/assets/BuilderSidebarToolkit-CApZNTAq.js +7 -0
  88. writer/static/assets/BuilderSidebarToolkit-CwqbjRv8.css +1 -0
  89. writer/static/assets/BuilderTemplateEditor-CYSDeWgV.css +1 -0
  90. writer/static/assets/BuilderTemplateEditor-DnRDRcA0.js +87 -0
  91. writer/static/assets/BuilderVault-2vGoV0sx.js +1 -0
  92. writer/static/assets/BuilderVault-Cx6oQSES.css +1 -0
  93. writer/static/assets/ComponentRenderer-72hqvEvI.css +1 -0
  94. writer/static/assets/ComponentRenderer-D4Pj1i3s.js +1 -0
  95. writer/static/assets/SharedCopyClipboardButton-BipJKGtz.css +1 -0
  96. writer/static/assets/SharedCopyClipboardButton-DNI9kLe6.js +1 -0
  97. writer/static/assets/WdsCheckbox-DKvpPA4D.css +1 -0
  98. writer/static/assets/WdsCheckbox-edQcn1cf.js +1 -0
  99. writer/static/assets/WdsDropdownMenu-CzzPN9Wg.css +1 -0
  100. writer/static/assets/WdsDropdownMenu-DQnrRBNV.js +1 -0
  101. writer/static/assets/WdsFieldWrapper-Cmufx5Nj.js +1 -0
  102. writer/static/assets/WdsFieldWrapper-CsemOh8D.css +1 -0
  103. writer/static/assets/WdsTabs-DKj7BqI0.css +1 -0
  104. writer/static/assets/WdsTabs-DcfY_zn5.js +1 -0
  105. writer/static/assets/{art-paper-WsD9P5Lu.svg → art-paper-D70v1WMA.svg} +0 -1
  106. writer/static/assets/{cssMode-6B7VrieQ.js → cssMode-BYq4oZGq.js} +1 -1
  107. writer/static/assets/{freemarker2-Dy54TNCQ.js → freemarker2-CnNourkO.js} +1 -1
  108. writer/static/assets/{handlebars-BnWqX2x5.js → handlebars-Bm22yapJ.js} +1 -1
  109. writer/static/assets/{html-CIuj_eOg.js → html-CAKAfoZF.js} +1 -1
  110. writer/static/assets/{htmlMode-5fUQN2xJ.js → htmlMode-BGZ97n-V.js} +1 -1
  111. writer/static/assets/index-BKNuk68o.css +1 -0
  112. writer/static/assets/index-BQNXU3IR.js +17 -0
  113. writer/static/assets/index-DHXAd5Yn.js +4 -0
  114. writer/static/assets/index-Zki-pfO-.js +8525 -0
  115. writer/static/assets/index.esm-B1ZQtduY.js +17 -0
  116. writer/static/assets/{javascript-PLzaI1wY.js → javascript-X1f02eyK.js} +1 -1
  117. writer/static/assets/{jsonMode-CzZfZ4-D.js → jsonMode-hT0bNgT8.js} +1 -1
  118. writer/static/assets/{liquid-Cy21vWLb.js → liquid-KmCCiJw2.js} +1 -1
  119. writer/static/assets/{mapbox-gl-BjXsUCYi.js → mapbox-gl-C0cyFYYW.js} +1 -1
  120. writer/static/assets/{mdx-Cgik3q5p.js → mdx-DtRFauUw.js} +1 -1
  121. writer/static/assets/pdf-B6-yWJ-Y.js +12 -0
  122. writer/static/assets/pdf.worker.min-CyUfim15.mjs +21 -0
  123. writer/static/assets/{plotly.min-Dk-1ahEu.js → plotly.min-DutuuatZ.js} +1 -1
  124. writer/static/assets/{python-h6gjz_bN.js → python-DVhxg746.js} +1 -1
  125. writer/static/assets/{razor-BQ1k9241.js → razor-DR5Ns_BC.js} +1 -1
  126. writer/static/assets/{tsMode-BaBWt05D.js → tsMode-BNUEZzir.js} +1 -1
  127. writer/static/assets/{typescript-B42-gYGT.js → typescript-CRVt7Hx0.js} +1 -1
  128. writer/static/assets/useBlueprintRun-C00bCxh-.js +1 -0
  129. writer/static/assets/useKeyValueEditor-nDmI7cTJ.js +1 -0
  130. writer/static/assets/useListResources-DLkZhRSJ.js +1 -0
  131. writer/static/assets/{xml-BOez4Prd.js → xml-C_6-t1tb.js} +1 -1
  132. writer/static/assets/{yaml-BQhoEOIz.js → yaml-DIw8G7jk.js} +1 -1
  133. writer/static/components/annotatedtext.svg +3 -3
  134. writer/static/components/avatar.svg +3 -3
  135. writer/static/components/blueprints_addtostatelist.svg +3 -3
  136. writer/static/components/blueprints_apitrigger.svg +4 -0
  137. writer/static/components/blueprints_category_Logic.svg +3 -3
  138. writer/static/components/blueprints_category_Other.svg +3 -3
  139. writer/static/components/blueprints_category_Writer.svg +24 -5
  140. writer/static/components/blueprints_code.svg +6 -6
  141. writer/static/components/blueprints_crontrigger.svg +6 -0
  142. writer/static/components/blueprints_foreach.svg +3 -3
  143. writer/static/components/blueprints_httprequest.svg +6 -6
  144. writer/static/components/blueprints_logmessage.svg +10 -3
  145. writer/static/components/blueprints_parsejson.svg +3 -3
  146. writer/static/components/blueprints_returnvalue.svg +3 -3
  147. writer/static/components/blueprints_runblueprint.svg +3 -3
  148. writer/static/components/blueprints_setstate.svg +3 -3
  149. writer/static/components/blueprints_writeraddchatmessage.svg +14 -6
  150. writer/static/components/blueprints_writeraddtokg.svg +14 -6
  151. writer/static/components/blueprints_writerchatreply.svg +19 -0
  152. writer/static/components/blueprints_writerclassification.svg +23 -8
  153. writer/static/components/blueprints_writercompletion.svg +13 -5
  154. writer/static/components/blueprints_writernocodeapp.svg +13 -3
  155. writer/static/components/button.svg +3 -3
  156. writer/static/components/category_Content.svg +3 -3
  157. writer/static/components/category_Embed.svg +3 -3
  158. writer/static/components/category_Input.svg +4 -4
  159. writer/static/components/category_Layout.svg +6 -6
  160. writer/static/components/category_Other.svg +3 -3
  161. writer/static/components/chatbot.svg +3 -3
  162. writer/static/components/checkboxinput.svg +3 -3
  163. writer/static/components/colorinput.svg +6 -6
  164. writer/static/components/column.svg +3 -3
  165. writer/static/components/columns.svg +3 -3
  166. writer/static/components/dataframe.svg +3 -3
  167. writer/static/components/dateinput.svg +3 -3
  168. writer/static/components/dropdowninput.svg +4 -4
  169. writer/static/components/fileinput.svg +3 -3
  170. writer/static/components/googlemaps.svg +3 -3
  171. writer/static/components/heading.svg +6 -6
  172. writer/static/components/horizontalstack.svg +3 -3
  173. writer/static/components/html.svg +6 -6
  174. writer/static/components/icon.svg +3 -3
  175. writer/static/components/iframe.svg +3 -3
  176. writer/static/components/image.svg +10 -3
  177. writer/static/components/jsonviewer.svg +3 -3
  178. writer/static/components/link.svg +7 -7
  179. writer/static/components/mapbox.svg +3 -3
  180. writer/static/components/message.svg +3 -3
  181. writer/static/components/metric.svg +3 -3
  182. writer/static/components/multiselectinput.svg +3 -3
  183. writer/static/components/numberinput.svg +3 -3
  184. writer/static/components/pagination.svg +3 -3
  185. writer/static/components/pdf.svg +3 -3
  186. writer/static/components/plotlygraph.svg +6 -6
  187. writer/static/components/progressbar.svg +4 -4
  188. writer/static/components/radioinput.svg +3 -3
  189. writer/static/components/rangeinput.svg +3 -3
  190. writer/static/components/ratinginput.svg +3 -3
  191. writer/static/components/repeater.svg +3 -3
  192. writer/static/components/reuse.svg +3 -3
  193. writer/static/components/section.svg +3 -3
  194. writer/static/components/selectinput.svg +4 -4
  195. writer/static/components/separator.svg +3 -3
  196. writer/static/components/sidebar.svg +3 -3
  197. writer/static/components/sliderinput.svg +3 -3
  198. writer/static/components/step.svg +3 -3
  199. writer/static/components/steps.svg +3 -3
  200. writer/static/components/switchinput.svg +3 -3
  201. writer/static/components/tab.svg +3 -3
  202. writer/static/components/tabs.svg +3 -3
  203. writer/static/components/tags.svg +10 -3
  204. writer/static/components/text.svg +3 -3
  205. writer/static/components/textareainput.svg +10 -3
  206. writer/static/components/textinput.svg +3 -3
  207. writer/static/components/timeinput.svg +3 -3
  208. writer/static/components/timer.svg +3 -3
  209. writer/static/components/videoplayer.svg +10 -3
  210. writer/static/components/webcamcapture.svg +3 -3
  211. writer/static/index.html +3 -11
  212. writer/static/status/cancelled.svg +5 -0
  213. writer/static/status/skipped.svg +4 -0
  214. writer/static/status/stopped.svg +4 -0
  215. writer/sync.py +431 -0
  216. writer/ui.py +49 -41
  217. writer/vault.py +48 -0
  218. writer/wf_project.py +5 -5
  219. writer-1.25.1rc1.dist-info/METADATA +92 -0
  220. writer-1.25.1rc1.dist-info/RECORD +382 -0
  221. {writer-0.8.3rc21.dist-info → writer-1.25.1rc1.dist-info}/WHEEL +1 -1
  222. writer/app_templates/default/.wf/components-blueprints_blueprint-0-t84xyhxau9ej3823.jsonl +0 -18
  223. writer/app_templates/default/static/welcome.svg +0 -40
  224. writer/static/assets/BaseMarkdown-BH_nSq9H.js +0 -1
  225. writer/static/assets/BlueprintToolbar-BO-WERxH.css +0 -1
  226. writer/static/assets/BlueprintToolbar-CHWL-rTm.js +0 -1
  227. writer/static/assets/BuilderApp-0XXiQ2l7.js +0 -7
  228. writer/static/assets/BuilderApp-CAhvLO4a.css +0 -1
  229. writer/static/assets/BuilderApplicationSelect-CwzU4F1-.js +0 -7
  230. writer/static/assets/BuilderApplicationSelect-DYYFtqjx.css +0 -1
  231. writer/static/assets/BuilderGraphSelect-CVO4gzts.css +0 -1
  232. writer/static/assets/BuilderGraphSelect-DV5Xy0HK.js +0 -7
  233. writer/static/assets/BuilderInstanceTracker-BECcXNnW.css +0 -1
  234. writer/static/assets/BuilderInstanceTracker-Dr0XhpSH.js +0 -1
  235. writer/static/assets/BuilderModelSelect-QHUGd86u.css +0 -1
  236. writer/static/assets/BuilderModelSelect-ow0XEf2t.js +0 -7
  237. writer/static/assets/BuilderSettings-C2WRfSor.css +0 -1
  238. writer/static/assets/BuilderSettings-D5bjbcWj.js +0 -24
  239. writer/static/assets/BuilderSettingsHandlers-1QLaHR8J.css +0 -1
  240. writer/static/assets/BuilderSettingsHandlers-j1aMAV4J.js +0 -1
  241. writer/static/assets/BuilderSidebarComponentTree-0YajaJke.css +0 -1
  242. writer/static/assets/BuilderSidebarComponentTree-CtaOZfpV.js +0 -1
  243. writer/static/assets/BuilderSidebarPanel-BMJVzhd3.js +0 -1
  244. writer/static/assets/BuilderSidebarPanel-BrLsNxVM.css +0 -1
  245. writer/static/assets/BuilderSidebarToolkit-BbjOOp8E.js +0 -1
  246. writer/static/assets/BuilderSidebarToolkit-BvZDShKD.css +0 -1
  247. writer/static/assets/ComponentRenderer-B76bKRZO.css +0 -1
  248. writer/static/assets/ComponentRenderer-CZs4z773.js +0 -1
  249. writer/static/assets/SharedMoreDropdown-BWKlox8E.css +0 -1
  250. writer/static/assets/SharedMoreDropdown-DKv_HNef.js +0 -7
  251. writer/static/assets/WdsDropdownMenu-C1UyKOJR.css +0 -1
  252. writer/static/assets/WdsDropdownMenu-CGiATY2E.js +0 -1
  253. writer/static/assets/WdsLoaderDots-qdyk2N-2.js +0 -1
  254. writer/static/assets/index-BJMAe9SN.js +0 -8
  255. writer/static/assets/index-CPCeQU9V.css +0 -1
  256. writer/static/assets/index-ChWW_c_j.js +0 -439
  257. writer/static/assets/instancePath-BsbOTTI8.js +0 -1
  258. writer/static/assets/material-symbols-outlined-latin-wght-normal-DuE-q1Ez.woff2 +0 -0
  259. writer/static/assets/useBlueprintRun-CBOvzWTA.js +0 -1
  260. writer/static/assets/useComponentDescription-FHKxu8gg.js +0 -1
  261. writer/static/assets/useListResources-BpMgq7XI.js +0 -1
  262. writer-0.8.3rc21.dist-info/METADATA +0 -117
  263. writer-0.8.3rc21.dist-info/RECORD +0 -342
  264. {writer-0.8.3rc21.dist-info → writer-1.25.1rc1.dist-info}/entry_points.txt +0 -0
  265. {writer-0.8.3rc21.dist-info → writer-1.25.1rc1.dist-info/licenses}/LICENSE.txt +0 -0
writer/command_line.py CHANGED
@@ -7,6 +7,7 @@ import click
7
7
  import writer.serve
8
8
  from writer import VERSION, wf_project
9
9
  from writer.deploy import cloud, deploy
10
+ from writer.sync import FileBuffering
10
11
 
11
12
  CONTEXT_SETTINGS = {'help_option_names': ['-h', '--help']}
12
13
  @click.group(
@@ -20,9 +21,8 @@ def main():
20
21
  @main.command()
21
22
  @click.option('--host', default="127.0.0.1", help="Host to run the app on")
22
23
  @click.option('--port', default=None, help="Port to run the app on")
23
- @click.option("--enable-jobs-api", help="Set this flag to enable the Jobs API, allowing you to execute jobs without user interaction.", is_flag=True)
24
24
  @click.argument('path')
25
- def run(path: str, host: str, port: Optional[int], enable_jobs_api: bool):
25
+ def run(path: str, host: str, port: Optional[int]):
26
26
  """Run the app from PATH folder in run mode."""
27
27
 
28
28
  abs_path = os.path.abspath(path)
@@ -35,15 +35,23 @@ def run(path: str, host: str, port: Optional[int], enable_jobs_api: bool):
35
35
  raise click.ClickException(f"There's no Writer Framework project at this location : {abs_path}")
36
36
 
37
37
  writer.serve.serve(
38
- abs_path, mode="run", port=port, host=host, enable_server_setup=True, enable_jobs_api=enable_jobs_api)
38
+ abs_path,
39
+ mode="run",
40
+ port=port,
41
+ host=host,
42
+ enable_server_setup=True
43
+ )
39
44
 
40
45
  @main.command()
41
46
  @click.option('--host', default="127.0.0.1", help="Host to run the app on")
42
47
  @click.option('--port', default=None, help="Port to run the app on")
43
48
  @click.option('--enable-remote-edit', help="Set this flag to allow non-local requests in edit mode.", is_flag=True)
44
49
  @click.option('--enable-server-setup', help="Set this flag to enable server setup hook in edit mode.", is_flag=True)
50
+ @click.option('--enable-file-buffering', help="Set this flag to enable file buffering in edit mode.", is_flag=True)
51
+ @click.option('--buffer-sync-interval', default=10, type=int, help="Interval in seconds for synchronizing the file buffer.")
52
+ @click.option('--buffer-path', default=None, type=str, help="Path to the file buffer directory. If not provided, a temporary directory will be used.")
45
53
  @click.option("--no-interactive", help="Set this flag to run the app without asking anything to the user.", is_flag=True)
46
- @click.option("--enable-jobs-api", help="Set this flag to enable the Jobs API, allowing you to execute jobs without user interaction.", is_flag=True)
54
+ @click.option('--verbose', '-v', is_flag=True, help="Enable verbose output.")
47
55
  @click.argument('path')
48
56
  def edit(
49
57
  path: str,
@@ -51,28 +59,56 @@ def edit(
51
59
  host: str,
52
60
  enable_remote_edit: bool,
53
61
  enable_server_setup: bool,
62
+ enable_file_buffering: bool,
63
+ buffer_sync_interval: int,
64
+ buffer_path: Optional[str],
54
65
  no_interactive: bool,
55
- enable_jobs_api: bool,
66
+ verbose: bool = False
56
67
  ):
57
68
  """Run the app from PATH folder in edit mode."""
58
- abs_path = os.path.abspath(path)
59
- if wf_project.is_project(path) is False and \
60
- wf_project.can_create_project(path) is True and \
69
+ buffer = None
70
+
71
+ if enable_file_buffering:
72
+ buffer = FileBuffering(
73
+ dest_dir=path,
74
+ src_dir=os.path.abspath(buffer_path) if buffer_path else None,
75
+ verbose=verbose,
76
+ interval=buffer_sync_interval,
77
+ )
78
+ print(f"Using temporary buffer path: {buffer.path}")
79
+ buffer.init()
80
+ buffer.watch_changes()
81
+ print("File buffering started in background")
82
+
83
+ project_path = buffer.path
84
+ else:
85
+ project_path = path
86
+
87
+
88
+ abs_path = os.path.abspath(project_path)
89
+ if wf_project.is_project(project_path) is False and \
90
+ wf_project.can_create_project(project_path) is True and \
61
91
  no_interactive is False:
62
92
  click.confirm("There’s no Writer Framework project at this location, would you like to create a new one ?", default=False, abort=True)
63
- create_app(path, template_name="default", overwrite=False)
93
+ create_app(project_path, template_name="default", overwrite=False)
64
94
 
65
- if wf_project.is_project(path) is False and \
66
- wf_project.can_create_project(path) is True:
95
+ if wf_project.is_project(project_path) is False and \
96
+ wf_project.can_create_project(project_path) is True:
67
97
  raise click.ClickException(f"There’s no Writer Framework project at this location, create a new one with `writer create {path}`")
68
98
 
69
- if wf_project.is_project(path) is False and \
70
- wf_project.can_create_project(path) is False:
99
+ if wf_project.is_project(project_path) is False and \
100
+ wf_project.can_create_project(project_path) is False:
71
101
  raise click.ClickException(f"There’s no Writer Framework project at this location : {abs_path}")
72
102
 
73
- writer.serve.serve(
74
- abs_path, mode="edit", port=port, host=host,
75
- enable_remote_edit=enable_remote_edit, enable_server_setup=enable_server_setup, enable_jobs_api=enable_jobs_api)
103
+ try:
104
+ writer.serve.serve(
105
+ abs_path, mode="edit", port=port, host=host,
106
+ enable_remote_edit=enable_remote_edit, enable_server_setup=enable_server_setup,
107
+ )
108
+ finally:
109
+ if buffer:
110
+ print("Stopping file buffering...")
111
+ buffer.stop()
76
112
 
77
113
  @main.command()
78
114
  @click.argument('path')
writer/core.py CHANGED
@@ -46,7 +46,9 @@ import writer.blocks
46
46
  import writer.evaluator
47
47
  from writer import core_ui
48
48
  from writer.core_ui import Component
49
+ from writer.logs import use_stdout_redirect
49
50
  from writer.ss_types import (
51
+ BlueprintExecutionError,
50
52
  BlueprintExecutionLog,
51
53
  InstancePath,
52
54
  Readable,
@@ -55,11 +57,13 @@ from writer.ss_types import (
55
57
  WriterEventResult,
56
58
  WriterFileItem,
57
59
  )
60
+ from writer.vault import writer_vault
58
61
 
59
62
  if TYPE_CHECKING:
60
63
  import pandas
61
64
 
62
65
  from writer.app_runner import AppProcess
66
+ from writer.blueprints import BlueprintRunner
63
67
  from writer.ss_types import AppProcessServerRequest
64
68
 
65
69
 
@@ -157,6 +161,7 @@ class WriterSession:
157
161
  self.session_component_tree = core_ui.build_session_component_tree(base_component_tree)
158
162
  self.event_handler = EventHandler(self)
159
163
  self.userinfo: Optional[dict] = None
164
+ self.queued_messages: List[Any] = []
160
165
 
161
166
  def update_last_active_timestamp(self) -> None:
162
167
  self.last_active_timestamp = int(time.time())
@@ -798,7 +803,7 @@ class State(metaclass=StateMeta):
798
803
  If there is a StateProxy, it is a fault in the code.
799
804
  """
800
805
  annotations = get_annotations(self)
801
- expected_type = annotations.get(key, None)
806
+ expected_type = cast(Type, annotations.get(key, None))
802
807
  expect_dict = _type_match_dict(expected_type)
803
808
  if isinstance(value, dict) and not expect_dict:
804
809
  """
@@ -1077,6 +1082,10 @@ class WriterState(State):
1077
1082
  shortened_message = message[0 : WriterState.LOG_ENTRY_MAX_LEN] + "..."
1078
1083
  else:
1079
1084
  shortened_message = message
1085
+
1086
+ if id is not None:
1087
+ self._remove_duplicates_in_mail(id)
1088
+
1080
1089
  self.add_mail(
1081
1090
  "logEntry",
1082
1091
  {
@@ -1104,6 +1113,18 @@ class WriterState(State):
1104
1113
  def clear_mail(self) -> None:
1105
1114
  self.mail = []
1106
1115
 
1116
+ def _remove_duplicates_in_mail(self, id: str) -> None:
1117
+ new_mail = []
1118
+ for entry in self.mail:
1119
+ if entry["type"] != "logEntry":
1120
+ new_mail.append(entry)
1121
+ continue
1122
+ log_id = entry["payload"].get("id")
1123
+ if log_id != id:
1124
+ new_mail.append(entry)
1125
+ continue
1126
+ self.mail = new_mail
1127
+
1107
1128
  def set_page(self, active_page_key: str) -> None:
1108
1129
  self.add_mail("pageChange", active_page_key)
1109
1130
 
@@ -1201,8 +1222,136 @@ class EventHandlerRegistry:
1201
1222
  callable: Callable
1202
1223
  meta: "EventHandlerRegistry.HandlerMeta"
1203
1224
 
1225
+ # === BLUEPRINT HANLDERS ===
1226
+ @staticmethod
1227
+ def stop_blueprint_run(payload: dict, blueprint_runner: "BlueprintRunner"):
1228
+ run_id = payload.pop("run_id", None)
1229
+ if not run_id:
1230
+ raise ValueError("Missing run_id in payload")
1231
+ return blueprint_runner.cancel_blueprint_execution(run_id=run_id)
1232
+
1233
+ @staticmethod
1234
+ def run_blueprint_by_id(
1235
+ payload: dict,
1236
+ context: dict,
1237
+ session: dict,
1238
+ blueprint_runner: "BlueprintRunner",
1239
+ vault: Dict,
1240
+ ):
1241
+ blueprint_id = payload.pop("blueprint_id", None)
1242
+ if not blueprint_id:
1243
+ raise ValueError("Missing blueprint_id in payload")
1244
+ execution_environment = EventHandler._get_blueprint_execution_environment(
1245
+ payload, context, session, vault
1246
+ )
1247
+ return blueprint_runner.run_blueprint(
1248
+ component_id=blueprint_id,
1249
+ execution_environment=execution_environment,
1250
+ title="Blueprint execution triggered on demand",
1251
+ )
1252
+
1253
+ @staticmethod
1254
+ def run_blueprint_by_key(
1255
+ payload: dict,
1256
+ context: dict,
1257
+ session: dict,
1258
+ blueprint_runner: "BlueprintRunner",
1259
+ vault: Dict,
1260
+ ):
1261
+ blueprint_key = payload.pop("blueprint_key", None)
1262
+ if not blueprint_key:
1263
+ raise ValueError("Missing blueprint_key in payload")
1264
+ execution_environment = EventHandler._get_blueprint_execution_environment(
1265
+ payload, context, session, vault
1266
+ )
1267
+ return blueprint_runner.run_blueprint_by_key(
1268
+ blueprint_key=blueprint_key, execution_environment=execution_environment
1269
+ )
1270
+
1271
+ @staticmethod
1272
+ def run_blueprint_via_api(
1273
+ payload: dict,
1274
+ context: dict,
1275
+ session: dict,
1276
+ blueprint_runner: "BlueprintRunner",
1277
+ vault: Dict,
1278
+ ):
1279
+ """
1280
+ This handler is used to run a blueprint via the API.
1281
+ It is used by the frontend to run a blueprint when the user clicks on a button.
1282
+ """
1283
+ blueprint_id = payload.pop("blueprint_id", None)
1284
+ if not blueprint_id:
1285
+ raise ValueError("Missing blueprint_id in payload")
1286
+ trigger_type = payload.pop("trigger_type", None)
1287
+ if not trigger_type:
1288
+ raise ValueError("Missing trigger_type in payload")
1289
+ execution_environment = EventHandler._get_blueprint_execution_environment(
1290
+ payload, context, session, vault
1291
+ )
1292
+ return blueprint_runner.run_blueprint_via_api(
1293
+ blueprint_id=blueprint_id,
1294
+ trigger_type=trigger_type,
1295
+ branch_id=payload.pop("branch_id", None),
1296
+ execution_environment=execution_environment,
1297
+ )
1298
+
1299
+ @staticmethod
1300
+ def run_blueprint_branch(
1301
+ payload: dict,
1302
+ context: dict,
1303
+ session: dict,
1304
+ blueprint_runner: "BlueprintRunner",
1305
+ vault: Dict,
1306
+ ):
1307
+ branch_id = payload.pop("branch_id", None)
1308
+ if not branch_id:
1309
+ raise ValueError("Missing branch_id in payload")
1310
+ execution_environment = EventHandler._get_blueprint_execution_environment(
1311
+ payload, context, session, vault
1312
+ )
1313
+ return blueprint_runner.run_branch(
1314
+ start_node_id=branch_id,
1315
+ branch_out_id=None,
1316
+ execution_environment=execution_environment,
1317
+ title="Branch execution triggered by demand",
1318
+ )
1319
+
1204
1320
  def __init__(self):
1205
- self.handler_map: Dict[str, "EventHandlerRegistry.HandlerEntry"] = {} # type: ignore
1321
+ self.handler_map: Dict[str, "EventHandlerRegistry.HandlerEntry"] = {
1322
+ "stop_blueprint_run": {
1323
+ "callable": self.stop_blueprint_run,
1324
+ "meta": {"name": "stop_blueprint_run", "args": ["payload", "blueprint_runner"]},
1325
+ },
1326
+ "run_blueprint_by_key": {
1327
+ "callable": self.run_blueprint_by_key,
1328
+ "meta": {
1329
+ "name": "run_blueprint_by_key",
1330
+ "args": ["payload", "context", "session", "blueprint_runner", "vault"],
1331
+ },
1332
+ },
1333
+ "run_blueprint_by_id": {
1334
+ "callable": self.run_blueprint_by_id,
1335
+ "meta": {
1336
+ "name": "run_blueprint_by_id",
1337
+ "args": ["payload", "context", "session", "blueprint_runner", "vault"],
1338
+ },
1339
+ },
1340
+ "run_blueprint_via_api": {
1341
+ "callable": self.run_blueprint_via_api,
1342
+ "meta": {
1343
+ "name": "run_blueprint_via_api",
1344
+ "args": ["payload", "context", "session", "blueprint_runner", "vault"],
1345
+ },
1346
+ },
1347
+ "run_blueprint_branch": {
1348
+ "callable": self.run_blueprint_branch,
1349
+ "meta": {
1350
+ "name": "run_blueprint_branch",
1351
+ "args": ["payload", "context", "session", "blueprint_runner", "vault"],
1352
+ },
1353
+ },
1354
+ }
1206
1355
 
1207
1356
  def __iter__(self):
1208
1357
  return iter(self.handler_map.keys())
@@ -1508,6 +1657,12 @@ class EventDeserialiser:
1508
1657
  payload = _deserialize_bigint_format(payload)
1509
1658
  return payload
1510
1659
 
1660
+ def _transform_tab_change(self, ev: WriterEvent) -> Optional[str]:
1661
+ payload = ev.payload
1662
+ if not isinstance(payload, str):
1663
+ return None
1664
+ return payload
1665
+
1511
1666
 
1512
1667
  class SessionManager:
1513
1668
  """
@@ -1639,16 +1794,29 @@ class EventHandler:
1639
1794
  return
1640
1795
  self.evaluator.set_state(binding["stateRef"], instance_path, payload)
1641
1796
 
1797
+ @staticmethod
1798
+ def _get_blueprint_execution_environment(payload, context, session, vault):
1799
+ return {
1800
+ "payload": payload,
1801
+ "context": context,
1802
+ "session": session,
1803
+ "vault": vault,
1804
+ }
1805
+
1642
1806
  def _get_blueprint_callable(
1643
1807
  self,
1644
1808
  blueprint_key: Optional[str] = None,
1645
1809
  blueprint_id: Optional[str] = None,
1646
1810
  branch_id: Optional[str] = None,
1647
1811
  ):
1648
- def fn(payload, context, session):
1649
- execution_environment = {"payload": payload, "context": context, "session": session}
1812
+ def fn(payload, context, session, vault):
1813
+ execution_environment = self._get_blueprint_execution_environment(
1814
+ payload, context, session, vault
1815
+ )
1650
1816
  if blueprint_key:
1651
- return self.blueprint_runner.run_blueprint_by_key(blueprint_key, execution_environment)
1817
+ return self.blueprint_runner.run_blueprint_by_key(
1818
+ blueprint_key, execution_environment
1819
+ )
1652
1820
  elif blueprint_id:
1653
1821
  return self.blueprint_runner.run_blueprint(
1654
1822
  blueprint_id, execution_environment, "Blueprint execution triggered on demand"
@@ -1664,18 +1832,6 @@ class EventHandler:
1664
1832
  return fn
1665
1833
 
1666
1834
  def _get_handler_callable(self, handler: str) -> Optional[Callable]:
1667
- if handler.startswith("$runBlueprint_"):
1668
- blueprint_key = handler[14:]
1669
- return self._get_blueprint_callable(blueprint_key=blueprint_key)
1670
-
1671
- if handler.startswith("$runBlueprintById_"):
1672
- blueprint_id = handler[18:]
1673
- return self._get_blueprint_callable(blueprint_id=blueprint_id)
1674
-
1675
- if handler.startswith("$runBlueprintTriggerBranchById_"):
1676
- branch_id = handler[31:]
1677
- return self._get_blueprint_callable(branch_id=branch_id)
1678
-
1679
1835
  current_app_process = get_app_process()
1680
1836
  handler_registry = current_app_process.handler_registry
1681
1837
  callable_handler = handler_registry.find_handler_callable(handler)
@@ -1690,24 +1846,23 @@ class EventHandler:
1690
1846
  "context": context_data,
1691
1847
  "session": _event_handler_session_info(),
1692
1848
  "ui": _event_handler_ui_manager(),
1849
+ "blueprint_runner": self.blueprint_runner,
1850
+ "vault": writer_vault.get_secrets(),
1693
1851
  }
1694
1852
 
1695
1853
  def _call_handler_callable(self, handler_callable: Callable, calling_arguments: Dict) -> Any:
1696
1854
  current_app_process = get_app_process()
1697
1855
  result = None
1698
- captured_stdout = None
1699
1856
  with (
1700
1857
  core_ui.use_component_tree(self.session.session_component_tree),
1701
- contextlib.redirect_stdout(io.StringIO()) as f,
1858
+ use_stdout_redirect(
1859
+ lambda entry: self.session_state.add_log_entry("info", "Stdout message", entry)
1860
+ ),
1702
1861
  ):
1703
1862
  middlewares_executors = current_app_process.middleware_registry.executors()
1704
1863
  result = EventHandlerExecutor.invoke_with_middlewares(
1705
1864
  middlewares_executors, handler_callable, calling_arguments
1706
1865
  )
1707
- captured_stdout = f.getvalue()
1708
-
1709
- if captured_stdout:
1710
- self.session_state.add_log_entry("info", "Stdout message", captured_stdout)
1711
1866
 
1712
1867
  return result
1713
1868
 
@@ -1740,17 +1895,21 @@ class EventHandler:
1740
1895
  calling_arguments = self._get_calling_arguments(ev, instance_path=None)
1741
1896
  return self._call_handler_callable(handler_callable, calling_arguments)
1742
1897
  except BaseException as e:
1743
- self.session_state.add_notification(
1744
- "error",
1745
- "Runtime Error",
1746
- f"An error occurred when processing event '{ ev.type }'.",
1747
- )
1748
- self.session_state.add_log_entry(
1749
- "error",
1750
- "Runtime Exception",
1751
- f"A runtime exception was raised when processing event '{ ev.type }'.",
1752
- traceback.format_exc(),
1753
- )
1898
+ if not isinstance(e, BlueprintExecutionError):
1899
+ # Only create a notification and log entry
1900
+ # for non-blueprint errors, as blueprint errors
1901
+ # have their own logging mechanism
1902
+ self.session_state.add_notification(
1903
+ "error",
1904
+ "Runtime Error",
1905
+ f"An error occurred when processing event '{ ev.type }'.",
1906
+ )
1907
+ self.session_state.add_log_entry(
1908
+ "error",
1909
+ "Runtime Exception",
1910
+ f"A runtime exception was raised when processing event '{ ev.type }'.",
1911
+ traceback.format_exc(),
1912
+ )
1754
1913
  raise e
1755
1914
 
1756
1915
  def _handle_component_event(self, ev: WriterEvent):
@@ -1762,7 +1921,13 @@ class EventHandler:
1762
1921
  target_component = cast(Component, self.session_component_tree.get_component(target_id))
1763
1922
  self._handle_binding(ev.type, target_component, instance_path, ev.payload)
1764
1923
  calling_arguments = self._get_calling_arguments(ev, instance_path)
1765
- self.blueprint_runner.execute_ui_trigger(target_id, ev.type, calling_arguments)
1924
+ execution_environment = self._get_blueprint_execution_environment(
1925
+ calling_arguments.get("payload"),
1926
+ calling_arguments.get("context"),
1927
+ calling_arguments.get("session"),
1928
+ calling_arguments.get("vault"),
1929
+ )
1930
+ self.blueprint_runner.execute_ui_trigger(target_id, ev.type, execution_environment)
1766
1931
  if not target_component.handlers:
1767
1932
  return None
1768
1933
  handler = target_component.handlers.get(ev.type)
writer/core_ui.py CHANGED
@@ -467,6 +467,10 @@ def export_component_tree(component_tree: ComponentTree, mode: ServeMode, only_u
467
467
  _components.append(_root_component)
468
468
  _components += component_tree.get_descendents(root)
469
469
 
470
+ # filters notes in run mode
471
+ if mode == "run":
472
+ _components = [c for c in _components if c.type != "note"]
473
+
470
474
  return {c.id: c.to_dict() for c in _components}
471
475
 
472
476
  class UIError(Exception):
writer/evaluator.py CHANGED
@@ -55,34 +55,32 @@ class Evaluator:
55
55
  raise ValueError(f'Component with id "{component_id}" not found.')
56
56
 
57
57
  field_value = component.content.get(field_key) or default_field_value
58
- replaced = None
59
58
  full_match = self.TEMPLATE_REGEX.fullmatch(field_value)
60
59
 
61
- def replacer(matched):
62
- if matched.string[0] == "\\": # Escaped @, don't evaluate
63
- return matched.string
60
+ def replacer(matched: re.Match):
61
+ if matched.group(0)[0] == "\\": # Escaped @, don't evaluate
62
+ return matched.group(0)
64
63
  expr = matched.group(1).strip()
65
64
  expr_value = self.evaluate_expression(expr, instance_path, base_context)
66
65
  if full_match is not None:
67
66
  return expr_value
68
67
  if as_json:
69
- return json.dumps(expr_value)
68
+ dumped = expr_value
69
+ if not isinstance(dumped, str):
70
+ dumped = json.dumps(dumped)
71
+ else:
72
+ dumped = json.dumps(dumped)[1:-1]
73
+ return re.sub(r'(?<!\\)"', r'\"', dumped)
70
74
  if not isinstance(expr_value, str):
71
75
  return json.dumps(expr_value)
72
76
  return expr_value
73
77
 
74
78
  if full_match is None:
75
- replaced = field_value
76
- if as_json:
77
- # First pass to remove quotes around @{my_var}
78
- replaced = re.sub(r'"(@{\s*[^"]+?\s*})"', r"\1", field_value)
79
- replaced = self.TEMPLATE_REGEX.sub(replacer, replaced)
80
- if as_json:
81
- replaced = decode_json(replaced)
79
+ replaced = self.TEMPLATE_REGEX.sub(replacer, field_value)
82
80
  else:
83
81
  replaced = replacer(full_match)
84
- if as_json:
85
- replaced = decode_json(replaced)
82
+ if as_json:
83
+ replaced = decode_json(replaced)
86
84
 
87
85
  return replaced
88
86
 
@@ -217,16 +215,7 @@ class Evaluator:
217
215
  state_ref: Any = self.state.user_state
218
216
  accessors: List[str] = self.parse_expression(expr, instance_path, base_context)
219
217
 
220
- for accessor in accessors:
221
- if isinstance(state_ref, (writer.core.StateProxy, dict)) and accessor in state_ref:
222
- state_ref = state_ref.get(accessor)
223
- result = state_ref
224
- elif isinstance(state_ref, (list)) and state_ref[int(accessor)] is not None:
225
- state_ref = state_ref[int(accessor)]
226
- result = state_ref
227
- elif isinstance(context_ref, dict) and accessor in context_ref:
228
- context_ref = context_ref.get(accessor)
229
- result = context_ref
218
+ result = self._apply_accessors(accessors, state_ref, context_ref)
230
219
 
231
220
  if isinstance(result, writer.core.StateProxy):
232
221
  return result.to_dict()
@@ -235,3 +224,28 @@ class Evaluator:
235
224
  return self.get_env_variable_value(expr)
236
225
 
237
226
  return result
227
+
228
+ def _apply_accessors(self, accessors: List[str], state_ref: Any, context_ref: Any = None) -> Any:
229
+ if not accessors:
230
+ return state_ref
231
+
232
+ result = self._apply_accessor(accessors[0], context_ref)
233
+ if result is None:
234
+ result = self._apply_accessor(accessors[0], state_ref)
235
+
236
+ for accessor in accessors[1:]:
237
+ result = self._apply_accessor(accessor, result)
238
+
239
+ return result
240
+
241
+ def _apply_accessor(self, accessor: str, target: Any) -> Any:
242
+ if isinstance(target, (writer.core.StateProxy, dict)):
243
+ return target.get(accessor)
244
+
245
+ if isinstance(target, list):
246
+ try:
247
+ return target[int(accessor)]
248
+ except IndexError:
249
+ pass
250
+
251
+ return None