Flowfile 0.5.6__py3-none-any.whl → 0.6.1__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 (256) hide show
  1. flowfile/api.py +8 -6
  2. flowfile/web/static/assets/{AdminView-c2c7942b.js → AdminView-C4K1DdHI.js} +28 -33
  3. flowfile/web/static/assets/{CloudConnectionView-7a3042c6.js → CloudConnectionView-BZbPvPUL.js} +39 -50
  4. flowfile/web/static/assets/{CloudStorageReader-24c54524.css → CloudStorageReader-BDByiqPI.css} +25 -25
  5. flowfile/web/static/assets/{CloudStorageReader-709c4037.js → CloudStorageReader-DLVukNJ7.js} +30 -35
  6. flowfile/web/static/assets/{CloudStorageWriter-604c51a8.js → CloudStorageWriter-Bfi-C1QW.js} +32 -37
  7. flowfile/web/static/assets/{CloudStorageWriter-60547855.css → CloudStorageWriter-y8jL8yjG.css} +24 -24
  8. flowfile/web/static/assets/{ColumnActionInput-d63d6746.js → ColumnActionInput-BpiCApw9.js} +7 -12
  9. flowfile/web/static/assets/{ColumnSelector-0c8cd1cd.js → ColumnSelector-CEAwedI7.js} +1 -2
  10. flowfile/web/static/assets/ContextMenu-CdojQu0w.js +9 -0
  11. flowfile/web/static/assets/ContextMenu-D12mhsy1.js +9 -0
  12. flowfile/web/static/assets/ContextMenu-EWUR98va.js +9 -0
  13. flowfile/web/static/assets/{ContextMenu.vue_vue_type_script_setup_true_lang-774c517c.js → ContextMenu.vue_vue_type_script_setup_true_lang-I4rXXd6G.js} +4 -5
  14. flowfile/web/static/assets/{CrossJoin-38e5b99a.js → CrossJoin-BOFfxkJO.js} +19 -18
  15. flowfile/web/static/assets/{CrossJoin-71b4cc10.css → CrossJoin-Cmbyt9im.css} +18 -18
  16. flowfile/web/static/assets/{CustomNode-76e8f3f5.js → CustomNode-Bhpezobq.js} +12 -17
  17. flowfile/web/static/assets/{DatabaseConnectionSettings-38155669.js → DatabaseConnectionSettings-Dw3bSJKB.js} +10 -11
  18. flowfile/web/static/assets/{DatabaseReader-5bf8c75b.css → DatabaseReader-D6pUNUCs.css} +21 -21
  19. flowfile/web/static/assets/{DatabaseReader-2e549c8f.js → DatabaseReader-m87ghlw0.js} +36 -34
  20. flowfile/web/static/assets/{DatabaseView-dc877c29.js → DatabaseView-CisSAtpe.js} +30 -38
  21. flowfile/web/static/assets/{DatabaseWriter-ffb91864.js → DatabaseWriter-Bbj9JLdL.js} +33 -35
  22. flowfile/web/static/assets/{DatabaseWriter-bdcf2c8b.css → DatabaseWriter-RBqdFLj8.css} +17 -17
  23. flowfile/web/static/assets/{DesignerView-a4466dab.js → DesignerView-DemDevTQ.js} +1752 -2054
  24. flowfile/web/static/assets/{DesignerView-71d4e9a1.css → DesignerView-Dm6OzlIc.css} +209 -168
  25. flowfile/web/static/assets/{DocumentationView-979afc84.js → DocumentationView-BrC1ZR3H.js} +3 -4
  26. flowfile/web/static/assets/{ExploreData-e4b92aaf.js → ExploreData-BMKcDuRb.js} +8 -10
  27. flowfile/web/static/assets/{ExternalSource-d08e7227.js → ExternalSource-BXrNNS-f.js} +40 -42
  28. flowfile/web/static/assets/{ExternalSource-7ac7373f.css → ExternalSource-NB6WVl5R.css} +14 -14
  29. flowfile/web/static/assets/{Filter-7add806d.js → Filter-C2MjsN6P.js} +36 -33
  30. flowfile/web/static/assets/{Filter-7494ea97.css → Filter-DCMGGuGC.css} +9 -9
  31. flowfile/web/static/assets/{Formula-53d58c43.css → Formula-BYafbDj8.css} +4 -4
  32. flowfile/web/static/assets/{Formula-36ab24d2.js → Formula-ufuy4mVD.js} +27 -26
  33. flowfile/web/static/assets/{FuzzyMatch-ad6361d6.css → FuzzyMatch-BGJAwgd0.css} +42 -42
  34. flowfile/web/static/assets/{FuzzyMatch-cc01bb04.js → FuzzyMatch-BOHODq3h.js} +36 -38
  35. flowfile/web/static/assets/{GraphSolver-4fb98f3b.js → GraphSolver-B6ZzpNGO.js} +23 -21
  36. flowfile/web/static/assets/{GraphSolver-4b4d7db9.css → GraphSolver-DFN83sj3.css} +4 -4
  37. flowfile/web/static/assets/{GroupBy-b3c8f429.js → GroupBy-B9BRNcfe.js} +30 -29
  38. flowfile/web/static/assets/{Sort-4abb7fae.css → GroupBy-x4ooP5np.css} +1 -1
  39. flowfile/web/static/assets/Join-Bx_g5bZz.css +118 -0
  40. flowfile/web/static/assets/{Join-096b7b26.js → Join-DsBEy1IH.js} +48 -43
  41. flowfile/web/static/assets/{LoginView-c33a246a.js → LoginView-Ct0rhdcO.js} +1 -2
  42. flowfile/web/static/assets/{ManualInput-39111f19.css → ManualInput-DlZmtMdt.css} +48 -48
  43. flowfile/web/static/assets/{ManualInput-7307e9b1.js → ManualInput-bC4BUgnG.js} +40 -41
  44. flowfile/web/static/assets/{MultiSelect-14822c48.js → MultiSelect-DIQ8PuTC.js} +2 -2
  45. flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-90c4d340.js → MultiSelect.vue_vue_type_script_setup_true_lang-BefHfqTI.js} +1 -1
  46. flowfile/web/static/assets/{NodeDesigner-5036c392.js → NodeDesigner-D39yzr2k.js} +178 -208
  47. flowfile/web/static/assets/{NodeDesigner-94cd4dd3.css → NodeDesigner-R0l6sYyY.css} +76 -76
  48. flowfile/web/static/assets/{NumericInput-15cf3b72.js → NumericInput-DMSX3oOr.js} +2 -2
  49. flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-91e679d7.js → NumericInput.vue_vue_type_script_setup_true_lang-d0YlVHAl.js} +1 -1
  50. flowfile/web/static/assets/{Output-1f8ed42c.js → Output-D0VoXGcW.js} +26 -34
  51. flowfile/web/static/assets/{Output-692dd25d.css → Output-DsmglIDy.css} +5 -5
  52. flowfile/web/static/assets/{Pivot-0e153f4e.js → Pivot-BnMB4sEe.js} +26 -26
  53. flowfile/web/static/assets/{Pivot-0eda81b4.css → Pivot-qKTyWxop.css} +4 -4
  54. flowfile/web/static/assets/{PivotValidation-81ec2a33.js → PivotValidation-B2lWvugt.js} +7 -9
  55. flowfile/web/static/assets/{PivotValidation-5a4f7c79.js → PivotValidation-BPlhRjpL.js} +7 -9
  56. flowfile/web/static/assets/{PolarsCode-a39f15ac.js → PolarsCode-5h0tHnWR.js} +22 -20
  57. flowfile/web/static/assets/{PopOver-ddcfe4f6.js → PopOver-BHpt5rsj.js} +5 -9
  58. flowfile/web/static/assets/{PopOver-d96599db.css → PopOver-CyYM4-rV.css} +1 -1
  59. flowfile/web/static/assets/{Read-90f366bc.css → Read-DJxkrTb_.css} +10 -10
  60. flowfile/web/static/assets/Read-TsLEFh3B.js +227 -0
  61. flowfile/web/static/assets/{RecordCount-e9048ccd.js → RecordCount-DkVixq9v.js} +18 -17
  62. flowfile/web/static/assets/{RecordId-ad02521d.js → RecordId-C2UEGlCf.js} +42 -39
  63. flowfile/web/static/assets/{SQLQueryComponent-2eeecf0b.js → SQLQueryComponent-Dr5KMoD3.js} +2 -3
  64. flowfile/web/static/assets/{Sample-9a68c23d.js → Sample-Cb3eQNmd.js} +30 -30
  65. flowfile/web/static/assets/{SecretSelector-2429f35a.js → SecretSelector-De2L2bSx.js} +3 -4
  66. flowfile/web/static/assets/{SecretsView-c6afc915.js → SecretsView-CheC9BPV.js} +13 -16
  67. flowfile/web/static/assets/{Select-fcd002b6.js → Select-CI8TloRs.js} +41 -36
  68. flowfile/web/static/assets/{SettingsSection-5ce15962.js → SettingsSection-B39ulIiI.js} +1 -2
  69. flowfile/web/static/assets/{SettingsSection-c6b1362c.js → SettingsSection-BiCc7S9h.js} +1 -2
  70. flowfile/web/static/assets/{SettingsSection-cebb91d5.js → SettingsSection-CITK_R7o.js} +2 -3
  71. flowfile/web/static/assets/{SettingsSection-26fe48d4.css → SettingsSection-D2GgY-Aq.css} +4 -4
  72. flowfile/web/static/assets/{SetupView-2d12e01f.js → SetupView-C1aXRDvp.js} +1 -2
  73. flowfile/web/static/assets/{SingleSelect-b67de4eb.js → SingleSelect-Kr_hz90m.js} +2 -2
  74. flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-eedb70eb.js → SingleSelect.vue_vue_type_script_setup_true_lang-Rxht5Z5N.js} +1 -1
  75. flowfile/web/static/assets/{SliderInput-fd8134ac.js → SliderInput-CLqpCxCb.js} +1 -2
  76. flowfile/web/static/assets/{GroupBy-5792782d.css → Sort-BIt2kc_p.css} +1 -1
  77. flowfile/web/static/assets/{Sort-c005a573.js → Sort-Dnw_J6Qi.js} +25 -25
  78. flowfile/web/static/assets/{TextInput-1bb31dab.js → TextInput-wdlunIZC.js} +2 -2
  79. flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-a51fe730.js → TextInput.vue_vue_type_script_setup_true_lang-Bcj3ywzv.js} +1 -1
  80. flowfile/web/static/assets/{TextToRows-4f363753.js → TextToRows-BhtyGWPq.js} +42 -49
  81. flowfile/web/static/assets/{TextToRows-12afb4f4.css → TextToRows-DivDOLDx.css} +9 -9
  82. flowfile/web/static/assets/{ToggleSwitch-ca0f2e5e.js → ToggleSwitch-B-6WzfFf.js} +2 -2
  83. flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-49aa41d8.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-Cj8LqT-b.js} +1 -1
  84. flowfile/web/static/assets/{UnavailableFields-f6147968.js → UnavailableFields-Yf6XSqFB.js} +2 -3
  85. flowfile/web/static/assets/{Union-c65f17b7.js → Union-CwpjeKYC.js} +20 -23
  86. flowfile/web/static/assets/{Unpivot-b6ad6427.css → Union-DQJcpp3-.css} +6 -6
  87. flowfile/web/static/assets/{Unique-a1d96fb2.js → Unique-25v3urqH.js} +75 -74
  88. flowfile/web/static/assets/{Union-d6a8d7d5.css → Unpivot-Deqh1gtI.css} +6 -6
  89. flowfile/web/static/assets/{Unpivot-c2657ff3.js → Unpivot-sYcTTXrq.js} +34 -27
  90. flowfile/web/static/assets/{UnpivotValidation-28e29a3b.js → UnpivotValidation-C5DDEKY2.js} +5 -7
  91. flowfile/web/static/assets/VueGraphicWalker-B8l1_Z92.js +131 -0
  92. flowfile/web/static/assets/VueGraphicWalker-Da_1-3me.css +21 -0
  93. flowfile/web/static/assets/{api-df48ec50.js → api-C0LvF-0C.js} +1 -1
  94. flowfile/web/static/assets/{api-ee542cf7.js → api-DaC83EO_.js} +1 -1
  95. flowfile/web/static/assets/client-C8Ygr6Gb.js +42 -0
  96. flowfile/web/static/assets/{dropDown-7576a76a.js → dropDown-D5YXaPRR.js} +7 -12
  97. flowfile/web/static/assets/{fullEditor-7583bef5.js → fullEditor-BVYnWm05.js} +300 -18
  98. flowfile/web/static/assets/genericNodeSettings-2wAu-QKn.css +75 -0
  99. flowfile/web/static/assets/genericNodeSettings-BBtW_Cpz.js +590 -0
  100. flowfile/web/static/assets/{VueGraphicWalker-2fc3ddd4.js → graphic-walker.es-VrK6vdGE.js} +92305 -89751
  101. flowfile/web/static/assets/index-BCJxPfM5.js +6693 -0
  102. flowfile/web/static/assets/{index-057d770d.js → index-CHPMUR0d.js} +150 -170
  103. flowfile/web/static/assets/index-DPkoZWq8.js +32 -0
  104. flowfile/web/static/assets/index-DnW_KC_I.js +277 -0
  105. flowfile/web/static/assets/index-UFXyfirV.css +10797 -0
  106. flowfile/web/static/assets/index-bcuE0Z0p.js +87456 -0
  107. flowfile/web/static/assets/{node.types-2c15bb7e.js → node.types-Dl4gtSW9.js} +2 -2
  108. flowfile/web/static/assets/{outputCsv-c492b15e.js → outputCsv-BELuBiJZ.js} +1 -2
  109. flowfile/web/static/assets/outputCsv-CdGkv-fN.css +2581 -0
  110. flowfile/web/static/assets/{outputExcel-13bfa10f.js → outputExcel-D0TTNM79.js} +1 -2
  111. flowfile/web/static/assets/{outputParquet-9be1523a.js → outputParquet-Cz9EbRHj.js} +1 -2
  112. flowfile/web/static/assets/{readCsv-5a49a8c9.js → readCsv-7bd3kUMI.js} +1 -2
  113. flowfile/web/static/assets/{readExcel-27c30ad8.js → readExcel-Cq8CCwIv.js} +3 -4
  114. flowfile/web/static/assets/{readParquet-c5244ad5.css → readParquet-CRDmBrsp.css} +4 -4
  115. flowfile/web/static/assets/{readParquet-446bde68.js → readParquet-DjR4mRaj.js} +4 -5
  116. flowfile/web/static/assets/{secrets.api-34431884.js → secrets.api-C9o2KE5V.js} +1 -1
  117. flowfile/web/static/assets/{selectDynamic-5754a2b1.js → selectDynamic-Bl5FVsME.js} +5 -7
  118. flowfile/web/static/assets/useNodeSettings-dMS9zmh_.js +69 -0
  119. flowfile/web/static/assets/{vue-codemirror.esm-8f46fb36.js → vue-codemirror.esm-CwaYwln0.js} +3469 -3064
  120. flowfile/web/static/assets/{vue-content-loader.es-808fe33a.js → vue-content-loader.es-CMoRXo7N.js} +3 -3
  121. flowfile/web/static/index.html +2 -3
  122. {flowfile-0.5.6.dist-info → flowfile-0.6.1.dist-info}/METADATA +2 -1
  123. flowfile-0.6.1.dist-info/RECORD +417 -0
  124. {flowfile-0.5.6.dist-info → flowfile-0.6.1.dist-info}/WHEEL +1 -1
  125. flowfile_core/auth/password.py +1 -0
  126. flowfile_core/database/init_db.py +7 -5
  127. flowfile_core/fileExplorer/funcs.py +2 -2
  128. flowfile_core/flowfile/code_generator/code_generator.py +13 -11
  129. flowfile_core/flowfile/filter_expressions.py +327 -0
  130. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +61 -59
  131. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +3 -29
  132. flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +45 -14
  133. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +20 -3
  134. flowfile_core/flowfile/flow_data_engine/subprocess_operations/streaming.py +206 -0
  135. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +146 -24
  136. flowfile_core/flowfile/flow_graph.py +504 -190
  137. flowfile_core/flowfile/flow_node/__init__.py +32 -0
  138. flowfile_core/flowfile/flow_node/executor.py +404 -0
  139. flowfile_core/flowfile/flow_node/flow_node.py +207 -106
  140. flowfile_core/flowfile/flow_node/models.py +40 -0
  141. flowfile_core/flowfile/flow_node/output_field_config_applier.py +217 -0
  142. flowfile_core/flowfile/flow_node/schema_utils.py +78 -0
  143. flowfile_core/flowfile/flow_node/state.py +155 -0
  144. flowfile_core/flowfile/history_manager.py +401 -0
  145. flowfile_core/flowfile/manage/compatibility_enhancements.py +9 -0
  146. flowfile_core/flowfile/manage/io_flowfile.py +3 -1
  147. flowfile_core/flowfile/sources/external_sources/sql_source/models.py +20 -4
  148. flowfile_core/flowfile/util/execution_orderer.py +89 -36
  149. flowfile_core/routes/auth.py +8 -9
  150. flowfile_core/routes/routes.py +320 -101
  151. flowfile_core/routes/user_defined_components.py +18 -16
  152. flowfile_core/schemas/history_schema.py +220 -0
  153. flowfile_core/schemas/input_schema.py +130 -6
  154. flowfile_core/schemas/schemas.py +9 -0
  155. flowfile_core/schemas/transform_schema.py +27 -5
  156. flowfile_core/schemas/yaml_types.py +23 -5
  157. flowfile_frame/adding_expr.py +18 -126
  158. flowfile_frame/callable_utils.py +261 -0
  159. flowfile_frame/database/connection_manager.py +0 -1
  160. flowfile_frame/expr.py +8 -4
  161. flowfile_frame/flow_frame.py +41 -41
  162. flowfile_frame/lazy.py +3 -12
  163. flowfile_frame/lazy_methods.py +5 -64
  164. flowfile_frame/utils.py +13 -32
  165. flowfile_worker/funcs.py +6 -4
  166. flowfile_worker/main.py +2 -0
  167. flowfile_worker/models.py +31 -11
  168. flowfile_worker/routes.py +60 -35
  169. flowfile_worker/spawner.py +7 -1
  170. flowfile_worker/streaming.py +335 -0
  171. flowfile/web/static/assets/ContextMenu-366bf1b4.js +0 -9
  172. flowfile/web/static/assets/ContextMenu-85cf5b44.js +0 -9
  173. flowfile/web/static/assets/ContextMenu-9d28ae6d.js +0 -9
  174. flowfile/web/static/assets/Join-28b5e18f.css +0 -109
  175. flowfile/web/static/assets/Read-39b63932.js +0 -222
  176. flowfile/web/static/assets/VueGraphicWalker-430f0b86.css +0 -6
  177. flowfile/web/static/assets/database_reader-ce1e55f3.svg +0 -24
  178. flowfile/web/static/assets/database_writer-b4ad0753.svg +0 -23
  179. flowfile/web/static/assets/element-icons-9c88a535.woff +0 -0
  180. flowfile/web/static/assets/element-icons-de5eb258.ttf +0 -0
  181. flowfile/web/static/assets/genericNodeSettings-0155288b.js +0 -136
  182. flowfile/web/static/assets/genericNodeSettings-3b2507ea.css +0 -46
  183. flowfile/web/static/assets/index-aeec439d.js +0 -38
  184. flowfile/web/static/assets/index-ca6799de.js +0 -62760
  185. flowfile/web/static/assets/index-d60c9dd4.css +0 -10777
  186. flowfile/web/static/assets/nodeInput-d478b9ac.js +0 -2
  187. flowfile/web/static/assets/outputCsv-cc84e09f.css +0 -2499
  188. flowfile-0.5.6.dist-info/RECORD +0 -407
  189. /flowfile/web/static/assets/{AdminView-f53bad23.css → AdminView-B2Dthl3u.css} +0 -0
  190. /flowfile/web/static/assets/{CloudConnectionView-cf85f943.css → CloudConnectionView-BdFYGWV7.css} +0 -0
  191. /flowfile/web/static/assets/{ColumnActionInput-c44b7aee.css → ColumnActionInput-dCasSIC9.css} +0 -0
  192. /flowfile/web/static/assets/{ColumnSelector-371637fb.css → ColumnSelector-j6sEOjo1.css} +0 -0
  193. /flowfile/web/static/assets/{CustomNode-edb9b939.css → CustomNode-VPlajG0j.css} +0 -0
  194. /flowfile/web/static/assets/{DatabaseConnectionSettings-c20a1e16.css → DatabaseConnectionSettings-B78hXYgu.css} +0 -0
  195. /flowfile/web/static/assets/{DatabaseView-6655afd6.css → DatabaseView-B-_adk1s.css} +0 -0
  196. /flowfile/web/static/assets/{DocumentationView-9ea6e871.css → DocumentationView-CL7iipFL.css} +0 -0
  197. /flowfile/web/static/assets/{ExploreData-10c5acc8.css → ExploreData-DHjv0Plr.css} +0 -0
  198. /flowfile/web/static/assets/{LoginView-d325d632.css → LoginView-DN1BXY3e.css} +0 -0
  199. /flowfile/web/static/assets/{PivotValidation-0e905b1a.css → PivotValidation-DK-FARWe.css} +0 -0
  200. /flowfile/web/static/assets/{PivotValidation-41b57ad6.css → PivotValidation-FUa9F47u.css} +0 -0
  201. /flowfile/web/static/assets/{PolarsCode-2b1f1f23.css → PolarsCode-G-gRSrSc.css} +0 -0
  202. /flowfile/web/static/assets/{SQLQueryComponent-edb90b98.css → SQLQueryComponent-oAbWw0r-.css} +0 -0
  203. /flowfile/web/static/assets/{SecretSelector-6329f743.css → SecretSelector-CJSadIZx.css} +0 -0
  204. /flowfile/web/static/assets/{SecretsView-aa291340.css → SecretsView-DbzIRAba.css} +0 -0
  205. /flowfile/web/static/assets/{SettingsSection-8f980839.css → SettingsSection-BGcJnH6q.css} +0 -0
  206. /flowfile/web/static/assets/{SettingsSection-07fbbc39.css → SettingsSection-DDWn_EGW.css} +0 -0
  207. /flowfile/web/static/assets/{SetupView-ec26f76a.css → SetupView-CI1nd-5Z.css} +0 -0
  208. /flowfile/web/static/assets/{SliderInput-f2e4f23c.css → SliderInput-BRk-q_Dk.css} +0 -0
  209. /flowfile/web/static/assets/{UnavailableFields-394a1f78.css → UnavailableFields-DRKDImKe.css} +0 -0
  210. /flowfile/web/static/assets/{Unique-2b705521.css → Unique-Absb0aON.css} +0 -0
  211. /flowfile/web/static/assets/{UnpivotValidation-d5ca3b7b.css → UnpivotValidation-DSBkFgS-.css} +0 -0
  212. /flowfile/web/static/assets/{airbyte-292aa232.png → airbyte-W0xvIXwZ.png} +0 -0
  213. /flowfile/web/static/assets/{cloud_storage_reader-aa1415d6.png → cloud_storage_reader-3GpSCk90.png} +0 -0
  214. /flowfile/web/static/assets/{cross_join-d30c0290.png → cross_join-B0qpgYoV.png} +0 -0
  215. /flowfile/web/static/assets/{dropDown-1d6acbd9.css → dropDown-CE0VF5_P.css} +0 -0
  216. /flowfile/web/static/assets/{explore_data-8a0a2861.png → explore_data-tX6olPPL.png} +0 -0
  217. /flowfile/web/static/assets/{fa-brands-400-808443ae.ttf → fa-brands-400-D1LuMI3I.ttf} +0 -0
  218. /flowfile/web/static/assets/{fa-brands-400-d7236a19.woff2 → fa-brands-400-D_cYUPeE.woff2} +0 -0
  219. /flowfile/web/static/assets/{fa-regular-400-e3456d12.woff2 → fa-regular-400-BjRzuEpd.woff2} +0 -0
  220. /flowfile/web/static/assets/{fa-regular-400-54cf6086.ttf → fa-regular-400-DZaxPHgR.ttf} +0 -0
  221. /flowfile/web/static/assets/{fa-solid-900-aa759986.woff2 → fa-solid-900-CTAAxXor.woff2} +0 -0
  222. /flowfile/web/static/assets/{fa-solid-900-d2f05935.ttf → fa-solid-900-D0aA9rwL.ttf} +0 -0
  223. /flowfile/web/static/assets/{fa-v4compatibility-0ce9033c.woff2 → fa-v4compatibility-C9RhG_FT.woff2} +0 -0
  224. /flowfile/web/static/assets/{fa-v4compatibility-30f6abf6.ttf → fa-v4compatibility-CCth-dXg.ttf} +0 -0
  225. /flowfile/web/static/assets/{filter-d7708bda.png → filter-WRdZyUOw.png} +0 -0
  226. /flowfile/web/static/assets/{formula-eeeb1611.png → formula-CgM7uHVI.png} +0 -0
  227. /flowfile/web/static/assets/{fullEditor-fe9f7e18.css → fullEditor-CmDI7T9F.css} +0 -0
  228. /flowfile/web/static/assets/{fuzzy_match-40c161b2.png → fuzzy_match-Yon3k5Tc.png} +0 -0
  229. /flowfile/web/static/assets/{graph_solver-8b7888b8.png → graph_solver-BlMrBttD.png} +0 -0
  230. /flowfile/web/static/assets/{group_by-80561fc3.png → group_by-Gici0CSS.png} +0 -0
  231. /flowfile/web/static/assets/{input_data-ab2eb678.png → input_data-BRdGecLc.png} +0 -0
  232. /flowfile/web/static/assets/{join-349043ae.png → join-BITWRu73.png} +0 -0
  233. /flowfile/web/static/assets/{manual_input-ae98f31d.png → manual_input-CFvo_EUS.png} +0 -0
  234. /flowfile/web/static/assets/{old_join-5d0eb604.png → old_join-B9bkpPqv.png} +0 -0
  235. /flowfile/web/static/assets/{output-06ec0371.png → output-Dp7-ZpC4.png} +0 -0
  236. /flowfile/web/static/assets/{outputExcel-f5d272b2.css → outputExcel-CKgRe2iT.css} +0 -0
  237. /flowfile/web/static/assets/{outputParquet-54597c3c.css → outputParquet-d7j407cK.css} +0 -0
  238. /flowfile/web/static/assets/{pivot-9660df51.png → pivot-DSxKhNlD.png} +0 -0
  239. /flowfile/web/static/assets/{polars_code-05ce5dc6.png → polars_code-DxiztZ1c.png} +0 -0
  240. /flowfile/web/static/assets/{readCsv-3bfac4c3.css → readCsv-BG-1Jilp.css} +0 -0
  241. /flowfile/web/static/assets/{readExcel-3db6b763.css → readExcel-DBQXKPtC.css} +0 -0
  242. /flowfile/web/static/assets/{record_count-dab44eb5.png → record_count-DCeaLtpS.png} +0 -0
  243. /flowfile/web/static/assets/{record_id-0b15856b.png → record_id-FeUjyIFh.png} +0 -0
  244. /flowfile/web/static/assets/{sample-693a88b5.png → sample-DeqfRiB-.png} +0 -0
  245. /flowfile/web/static/assets/{select-b0d0437a.png → select-D4JjbdjS.png} +0 -0
  246. /flowfile/web/static/assets/{selectDynamic-f2fb394f.css → selectDynamic-CjeTPUUo.css} +0 -0
  247. /flowfile/web/static/assets/{sort-2aa579f0.png → sort-DGwUG9WS.png} +0 -0
  248. /flowfile/web/static/assets/{summarize-2a099231.png → summarize-DFaNHpfp.png} +0 -0
  249. /flowfile/web/static/assets/{text_to_rows-859b29ea.png → text_to_rows-BdiAewrN.png} +0 -0
  250. /flowfile/web/static/assets/{union-2d8609f4.png → union-DCK-LSMq.png} +0 -0
  251. /flowfile/web/static/assets/{unique-1958b98a.png → unique-CdP3zZIq.png} +0 -0
  252. /flowfile/web/static/assets/{unpivot-d3cb4b5b.png → unpivot-CHttrEt8.png} +0 -0
  253. /flowfile/web/static/assets/{user-defined-icon-0ae16c90.png → user-defined-icon-BcIp2Vzo.png} +0 -0
  254. /flowfile/web/static/assets/{view-7a0f0be1.png → view-DUSRwjvq.png} +0 -0
  255. {flowfile-0.5.6.dist-info → flowfile-0.6.1.dist-info}/entry_points.txt +0 -0
  256. {flowfile-0.5.6.dist-info → flowfile-0.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -17,6 +17,7 @@ from flowfile_core.flowfile.flow_data_engine.subprocess_operations import (
17
17
  get_external_df_result,
18
18
  results_exists,
19
19
  )
20
+ from flowfile_core.flowfile.flow_node.executor import NodeExecutor
20
21
  from flowfile_core.flowfile.flow_node.models import (
21
22
  NodeResults,
22
23
  NodeSchemaInformation,
@@ -24,7 +25,10 @@ from flowfile_core.flowfile.flow_node.models import (
24
25
  NodeStepSettings,
25
26
  NodeStepStats,
26
27
  )
28
+ from flowfile_core.flowfile.flow_node.output_field_config_applier import apply_output_field_config
27
29
  from flowfile_core.flowfile.flow_node.schema_callback import SingleExecutionFuture
30
+ from flowfile_core.flowfile.flow_node.schema_utils import create_schema_callback_with_output_config
31
+ from flowfile_core.flowfile.flow_node.state import NodeExecutionState
28
32
  from flowfile_core.flowfile.setting_generator import setting_generator, setting_updator
29
33
  from flowfile_core.flowfile.utils import get_hash
30
34
  from flowfile_core.schemas import input_schema, schemas
@@ -63,6 +67,8 @@ class FlowNode:
63
67
  _cache_progress: (
64
68
  ExternalDfFetcher | ExternalDatabaseFetcher | ExternalDatabaseWriter | ExternalCloudWriter | None
65
69
  ) = None
70
+ _execution_state: NodeExecutionState = None
71
+ _executor: NodeExecutor | None = None # Lazy-initialized
66
72
 
67
73
  def __init__(
68
74
  self,
@@ -130,6 +136,9 @@ class FlowNode:
130
136
  self._schema_callback = None
131
137
  self._state_needs_reset = False
132
138
  self._execution_lock = threading.RLock() # Protects concurrent access to get_resulting_data
139
+ # Initialize execution state
140
+ self._execution_state = NodeExecutionState()
141
+ self._executor = None # Will be lazily created
133
142
 
134
143
  @property
135
144
  def state_needs_reset(self) -> bool:
@@ -192,12 +201,20 @@ class FlowNode:
192
201
  def schema_callback(self, f: Callable):
193
202
  """Sets the schema callback function for the node.
194
203
 
204
+ If the node has an enabled output_field_config, the callback is automatically
205
+ wrapped to use the output_field_config schema for prediction.
206
+
195
207
  Args:
196
208
  f: The function to be used for schema calculation.
197
209
  """
198
210
  if f is None:
199
211
  return
200
212
 
213
+ # Wrap callback with output_field_config support if present and enabled
214
+ output_field_config = getattr(self._setting_input, 'output_field_config', None)
215
+ if output_field_config and output_field_config.enabled:
216
+ f = create_schema_callback_with_output_config(f, output_field_config)
217
+
201
218
  def error_callback(e: Exception) -> list:
202
219
  logger.warning(e)
203
220
 
@@ -206,6 +223,17 @@ class FlowNode:
206
223
 
207
224
  self._schema_callback = SingleExecutionFuture(f, error_callback)
208
225
 
226
+ @property
227
+ def executor(self) -> NodeExecutor:
228
+ """Lazy-initialized executor instance.
229
+
230
+ Reusing the same executor avoids object creation overhead
231
+ when execute_node is called multiple times.
232
+ """
233
+ if self._executor is None:
234
+ self._executor = NodeExecutor(self)
235
+ return self._executor
236
+
209
237
  @property
210
238
  def is_start(self) -> bool:
211
239
  """Determines if the node is a starting node in the flow.
@@ -328,6 +356,9 @@ class FlowNode:
328
356
  if is_manual_input:
329
357
  _ = self.hash
330
358
  self._setting_input = setting_input
359
+ # Copy cache_results from setting_input to node_settings
360
+ if hasattr(setting_input, "cache_results"):
361
+ self.node_settings.cache_results = setting_input.cache_results
331
362
  self.set_node_information()
332
363
  if is_manual_input:
333
364
  if self.hash != self.calculate_hash(setting_input) or not self.node_stats.has_run_with_current_setup:
@@ -393,7 +424,6 @@ class FlowNode:
393
424
 
394
425
  This includes the node's connections, settings, and position.
395
426
  """
396
- logger.info("setting node information")
397
427
  node_information = self.node_information
398
428
  node_information.left_input_id = self.node_inputs.left_input.node_id if self.left_input else None
399
429
  node_information.right_input_id = self.node_inputs.right_input.node_id if self.right_input else None
@@ -405,6 +435,9 @@ class FlowNode:
405
435
  node_information.description = (
406
436
  self.setting_input.description if hasattr(self.setting_input, "description") else ""
407
437
  )
438
+ node_information.node_reference = (
439
+ self.setting_input.node_reference if hasattr(self.setting_input, "node_reference") else None
440
+ )
408
441
  node_information.is_setup = self.is_setup
409
442
  node_information.x_position = self.setting_input.pos_x
410
443
  node_information.y_position = self.setting_input.pos_y
@@ -534,23 +567,60 @@ class FlowNode:
534
567
  Returns:
535
568
  A list of FlowfileColumn objects representing the predicted schema.
536
569
  """
570
+ logger.info(
571
+ f"get_predicted_schema: node_id={self.node_id}, node_type={self.node_type}, force={force}, "
572
+ f"has_predicted_schema={self.node_schema.predicted_schema is not None}, "
573
+ f"has_schema_callback={self.schema_callback is not None}, "
574
+ f"has_output_field_config={hasattr(self._setting_input, 'output_field_config') and self._setting_input.output_field_config is not None if self._setting_input else False}"
575
+ )
537
576
 
538
577
  if self.node_schema.predicted_schema and not force:
578
+ logger.debug(f"get_predicted_schema: node_id={self.node_id} - returning cached predicted_schema")
539
579
  return self.node_schema.predicted_schema
580
+
540
581
  if self.schema_callback is not None and (self.node_schema.predicted_schema is None or force):
541
582
  self.print("Getting the data from a schema callback")
583
+ logger.info(f"get_predicted_schema: node_id={self.node_id} - invoking schema_callback")
542
584
  if force:
543
585
  # Force the schema callback to reset, so that it will be executed again
586
+ logger.debug(f"get_predicted_schema: node_id={self.node_id} - forcing schema_callback reset")
544
587
  self.schema_callback.reset()
545
- schema = self.schema_callback()
588
+
589
+ try:
590
+ schema = self.schema_callback()
591
+ logger.info(
592
+ f"get_predicted_schema: node_id={self.node_id} - schema_callback returned "
593
+ f"{len(schema) if schema else 0} columns: {[c.name for c in schema] if schema else []}"
594
+ )
595
+ except Exception as e:
596
+ logger.error(f"get_predicted_schema: node_id={self.node_id} - schema_callback raised exception: {e}")
597
+ schema = None
598
+
546
599
  if schema is not None and len(schema) > 0:
547
600
  self.print("Calculating the schema based on the schema callback")
548
601
  self.node_schema.predicted_schema = schema
602
+ logger.info(f"get_predicted_schema: node_id={self.node_id} - set predicted_schema from schema_callback")
549
603
  return self.node_schema.predicted_schema
604
+ else:
605
+ logger.warning(f"get_predicted_schema: node_id={self.node_id} - schema_callback returned empty/None schema")
606
+ else:
607
+ logger.debug(f"get_predicted_schema: node_id={self.node_id} - no schema_callback available")
608
+
609
+ logger.debug(f"get_predicted_schema: node_id={self.node_id} - falling back to _predicted_data_getter")
550
610
  predicted_data = self._predicted_data_getter()
551
611
  if predicted_data is not None and predicted_data.schema is not None:
552
612
  self.print("Calculating the schema based on the predicted resulting data")
613
+ logger.info(
614
+ f"get_predicted_schema: node_id={self.node_id} - using schema from predicted_data "
615
+ f"({len(predicted_data.schema)} columns)"
616
+ )
553
617
  self.node_schema.predicted_schema = self._predicted_data_getter().schema
618
+ else:
619
+ logger.warning(
620
+ f"get_predicted_schema: node_id={self.node_id} - no schema available from any source "
621
+ f"(predicted_data={'None' if predicted_data is None else 'has_data'}, "
622
+ f"schema={'None' if predicted_data is None or predicted_data.schema is None else 'has_schema'})"
623
+ )
554
624
 
555
625
  return self.node_schema.predicted_schema
556
626
 
@@ -579,7 +649,8 @@ class FlowNode:
579
649
  """Executes the node's function to produce the actual output data.
580
650
 
581
651
  Handles both regular functions and external data sources.
582
- Thread-safe: uses _execution_lock to prevent concurrent execution.
652
+ Thread-safe: uses _execution_lock to prevent concurrent execution
653
+ and concurrent access to the underlying LazyFrame by sibling nodes.
583
654
 
584
655
  Returns:
585
656
  A FlowDataEngine instance containing the result, or None on error.
@@ -602,17 +673,34 @@ class FlowNode:
602
673
  try:
603
674
  self.print("Collecting input data from all inputs")
604
675
  input_data = []
605
- for i, v in enumerate(self.all_inputs):
606
- self.print(f"Getting resulting data from input {i} (node {v.node_id})")
607
- input_result = v.get_resulting_data()
608
- self.print(f"Input {i} data type: {type(input_result)}, dataframe type: {type(input_result.data_frame) if input_result else 'None'}")
609
- input_data.append(input_result)
610
- self.print(f"All {len(input_data)} inputs collected, calling node function")
611
- fl = self._function(*input_data)
612
- self.print(f"Node function returned, result type: {type(fl)}")
676
+ input_locks = []
677
+ try:
678
+ for i, v in enumerate(self.all_inputs):
679
+ self.print(f"Getting resulting data from input {i} (node {v.node_id})")
680
+ # Lock the input node to prevent sibling nodes from
681
+ # concurrently accessing the same upstream LazyFrame.
682
+ v._execution_lock.acquire()
683
+ input_locks.append(v._execution_lock)
684
+ input_result = v.get_resulting_data()
685
+ self.print(f"Input {i} data type: {type(input_result)}, dataframe type: {type(input_result.data_frame) if input_result else 'None'}")
686
+ input_data.append(input_result)
687
+ self.print(f"All {len(input_data)} inputs collected, calling node function")
688
+ fl = self._function(*input_data)
689
+ finally:
690
+ for lock in input_locks:
691
+ lock.release()
613
692
  except Exception as e:
614
693
  raise e
615
694
  fl.set_streamable(self.node_settings.streamable)
695
+
696
+ # Apply output field configuration if enabled
697
+ if hasattr(self._setting_input, 'output_field_config') and self._setting_input.output_field_config:
698
+ try:
699
+ fl = apply_output_field_config(fl, self._setting_input.output_field_config)
700
+ except Exception as e:
701
+ logger.error(f"Error applying output field config for node {self.node_id}: {e}")
702
+ raise
703
+
616
704
  self.results.resulting_data = fl
617
705
  self.node_schema.result_schema = fl.schema
618
706
  except Exception as e:
@@ -633,6 +721,13 @@ class FlowNode:
633
721
  """
634
722
  try:
635
723
  fl = self._function(*[v.get_predicted_resulting_data() for v in self.all_inputs])
724
+
725
+ # Apply output field configuration if enabled (mirrors get_resulting_data behavior)
726
+ # This ensures schema prediction accounts for output_field_config validation
727
+ if hasattr(self._setting_input, 'output_field_config') and self._setting_input.output_field_config:
728
+ if self._setting_input.output_field_config.enabled:
729
+ fl = apply_output_field_config(fl, self._setting_input.output_field_config)
730
+
636
731
  return fl
637
732
  except ValueError as e:
638
733
  if str(e) == "generator already executing":
@@ -768,9 +863,50 @@ class FlowNode:
768
863
  """Makes the node instance callable, acting as an alias for execute_node."""
769
864
  self.execute_node(*args, **kwargs)
770
865
 
771
- def execute_full_local(self, performance_mode: bool = False) -> None:
866
+ def _can_skip_execution_fast(
867
+ self,
868
+ run_location: schemas.ExecutionLocationsLiteral,
869
+ performance_mode: bool,
870
+ reset_cache: bool,
871
+ ) -> bool:
872
+ """Fast-path check to avoid executor overhead when we can skip.
873
+
874
+ This inlines the most common skip conditions to avoid
875
+ creating an executor instance when not needed.
876
+
877
+ Returns True if execution can definitely be skipped.
878
+ Returns False if full execution logic is needed.
879
+ """
880
+ # Can't skip if forced refresh
881
+ if reset_cache:
882
+ return False
883
+
884
+ # Output nodes always run
885
+ if self.node_template.node_group == "output":
886
+ return False
887
+
888
+ # Must run if never ran before
889
+ if not self._execution_state.has_run_with_current_setup:
890
+ return False
891
+
892
+ # Check for source file changes (read nodes only)
893
+ if self.node_type == "read" and self._execution_state.source_file_info:
894
+ if self._execution_state.source_file_info.has_changed():
895
+ return False
896
+
897
+ # Cache-enabled nodes: only skip if the cache file is still present
898
+ if self.node_settings.cache_results:
899
+ return results_exists(self.hash)
900
+
901
+ # Already ran with current settings → skip
902
+ # Results are available in memory from previous execution
903
+ return True
904
+
905
+ def _do_execute_full_local(self, performance_mode: bool = False) -> None:
772
906
  """Executes the node's logic locally, including example data generation.
773
907
 
908
+ Internal method called by NodeExecutor.
909
+
774
910
  Args:
775
911
  performance_mode: If True, skips generating example data.
776
912
 
@@ -798,12 +934,14 @@ class FlowNode:
798
934
  self.node_schema.result_schema = self.results.resulting_data.schema
799
935
  self.node_stats.has_completed_last_run = True
800
936
 
801
- def execute_local(self, flow_id: int, performance_mode: bool = False):
802
- """Executes the node's logic locally.
937
+ def _do_execute_local_with_sampling(self, performance_mode: bool = False, flow_id: int = None):
938
+ """Executes the node's logic locally with external sampling.
939
+
940
+ Internal method called by NodeExecutor.
803
941
 
804
942
  Args:
805
- flow_id: The ID of the parent flow.
806
943
  performance_mode: If True, skips generating example data.
944
+ flow_id: The ID of the parent flow.
807
945
 
808
946
  Raises:
809
947
  Exception: Propagates exceptions from the execution.
@@ -836,9 +974,11 @@ class FlowNode:
836
974
  if not self.node_settings.streamable:
837
975
  step.node_settings.streamable = self.node_settings.streamable
838
976
 
839
- def execute_remote(self, performance_mode: bool = False, node_logger: NodeLogger = None):
977
+ def _do_execute_remote(self, performance_mode: bool = False, node_logger: NodeLogger = None):
840
978
  """Executes the node's logic remotely or handles cached results.
841
979
 
980
+ Internal method called by NodeExecutor.
981
+
842
982
  Args:
843
983
  performance_mode: If True, skips generating example data.
844
984
  node_logger: The logger for this node execution.
@@ -920,6 +1060,19 @@ class FlowNode:
920
1060
  finally:
921
1061
  self._fetch_cached_df = None
922
1062
 
1063
+ # Backward-compatible aliases for renamed methods
1064
+ def execute_full_local(self, performance_mode: bool = False) -> None:
1065
+ """Backward-compatible alias for _do_execute_full_local."""
1066
+ return self._do_execute_full_local(performance_mode)
1067
+
1068
+ def execute_local(self, flow_id: int, performance_mode: bool = False):
1069
+ """Backward-compatible alias for _do_execute_local_with_sampling."""
1070
+ return self._do_execute_local_with_sampling(performance_mode, flow_id)
1071
+
1072
+ def execute_remote(self, performance_mode: bool = False, node_logger: NodeLogger = None):
1073
+ """Backward-compatible alias for _do_execute_remote."""
1074
+ return self._do_execute_remote(performance_mode, node_logger)
1075
+
923
1076
  def prepare_before_run(self):
924
1077
  """Resets results and errors before a new execution."""
925
1078
 
@@ -945,103 +1098,42 @@ class FlowNode:
945
1098
  retry: bool = True,
946
1099
  node_logger: NodeLogger = None,
947
1100
  optimize_for_downstream: bool = True,
948
- ):
949
- """Orchestrates the execution, handling location, caching, and retries.
1101
+ ) -> None:
1102
+ """Execute the node based on its current state and settings.
950
1103
 
951
- Args:
952
- run_location: The location for execution ('local', 'remote').
953
- reset_cache: If True, forces removal of any existing cache.
954
- performance_mode: If True, optimizes for speed over diagnostics.
955
- retry: If True, allows retrying execution on recoverable errors.
956
- node_logger: The logger for this node execution.
957
- optimize_for_downstream: If true, operations that shuffle the order of rows are fully cached and provided as
958
- input to downstream steps
1104
+ This method uses a fast-path to quickly skip execution when possible,
1105
+ avoiding executor overhead. For cases requiring full execution logic,
1106
+ it delegates to the NodeExecutor.
959
1107
 
960
- Raises:
961
- Exception: If the node_logger is not defined.
1108
+ Args:
1109
+ run_location: Where to execute ('local' or 'remote')
1110
+ reset_cache: Force cache invalidation
1111
+ performance_mode: Skip example data generation for speed
1112
+ retry: Allow retry on recoverable errors
1113
+ node_logger: Logger for this node's execution
1114
+ optimize_for_downstream: Cache wide transforms for downstream nodes
962
1115
  """
963
1116
  if node_logger is None:
964
- raise Exception("Flow logger is not defined")
965
- # TODO: Simplify which route is being picked there are many duplicate checks
1117
+ raise ValueError("node_logger is required")
966
1118
 
967
- if reset_cache:
968
- self.remove_cache()
969
- self.node_stats.has_run_with_current_setup = False
970
- self.node_stats.has_completed_last_run = False
1119
+ if not self.is_setup:
1120
+ node_logger.warning(f"Node {self.__name__} is not setup, cannot run")
1121
+ return
971
1122
 
972
- if self.is_setup:
973
- node_logger.info(f"Starting to run {self.__name__}")
974
- if (
975
- self.needs_run(performance_mode, node_logger, run_location)
976
- or self.node_template.node_group == "output"
977
- and not (run_location == "local")
978
- ):
979
- self.clear_table_example()
980
- self.prepare_before_run()
981
- self.reset()
982
- try:
983
- if (
984
- run_location == "remote"
985
- or (self.node_default.transform_type == "wide" and optimize_for_downstream)
986
- and not run_location == "local"
987
- ) or self.node_settings.cache_results:
988
- node_logger.info("Running the node remotely")
989
- if self.node_settings.cache_results:
990
- performance_mode = False
991
- self.execute_remote(
992
- performance_mode=(performance_mode if not self.node_settings.cache_results else False),
993
- node_logger=node_logger,
994
- )
995
- else:
996
- node_logger.info("Running the node locally")
997
- self.execute_local(performance_mode=performance_mode, flow_id=node_logger.flow_id)
998
- except Exception as e:
999
- if "No such file or directory (os error" in str(e) and retry:
1000
- logger.warning("Error with the input node, starting to rerun the input node...")
1001
- all_inputs: list[FlowNode] = self.node_inputs.get_all_inputs()
1002
- for node_input in all_inputs:
1003
- node_input.execute_node(
1004
- run_location=run_location,
1005
- performance_mode=performance_mode,
1006
- retry=True,
1007
- reset_cache=True,
1008
- node_logger=node_logger,
1009
- )
1010
- self.execute_node(
1011
- run_location=run_location,
1012
- performance_mode=performance_mode,
1013
- retry=False,
1014
- node_logger=node_logger,
1015
- )
1016
- else:
1017
- self.results.errors = str(e)
1018
- if "Connection refused" in str(e) and "/submit_query/" in str(e):
1019
- node_logger.warning(
1020
- "There was an issue connecting to the remote worker, "
1021
- "ensure the worker process is running, "
1022
- "or change the settings to, so it executes locally"
1023
- )
1024
- node_logger.error(
1025
- "Could not execute in the remote worker. (Re)start the worker service, or change settings to local settings."
1026
- )
1027
- else:
1028
- node_logger.error(f"Error with running the node: {e}")
1029
- elif (run_location == "local") and (
1030
- not self.node_stats.has_run_with_current_setup or self.node_template.node_group == "output"
1031
- ):
1032
- try:
1033
- node_logger.info("Executing fully locally")
1034
- self.execute_full_local(performance_mode)
1035
- except Exception as e:
1036
- self.results.errors = str(e)
1037
- node_logger.error(f"Error with running the node: {e}")
1038
- self.node_stats.error = str(e)
1039
- self.node_stats.has_completed_last_run = False
1123
+ # Fast-path: check if we can skip without creating executor
1124
+ if self._can_skip_execution_fast(run_location, performance_mode, reset_cache):
1125
+ node_logger.info("Node is up-to-date, skipping execution")
1126
+ return
1040
1127
 
1041
- else:
1042
- node_logger.info("Node has already run, not running the node")
1043
- else:
1044
- node_logger.warning(f"Node {self.__name__} is not setup, cannot run the node")
1128
+ # Full execution logic via executor
1129
+ self.executor.execute(
1130
+ run_location=run_location,
1131
+ reset_cache=reset_cache,
1132
+ performance_mode=performance_mode,
1133
+ retry=retry,
1134
+ node_logger=node_logger,
1135
+ optimize_for_downstream=optimize_for_downstream,
1136
+ )
1045
1137
 
1046
1138
  def store_example_data_generator(self, external_df_fetcher: ExternalDfFetcher | ExternalSampler):
1047
1139
  """Stores a generator function for fetching a sample of the result data.
@@ -1082,6 +1174,15 @@ class FlowNode:
1082
1174
  self._hash = None
1083
1175
  self.node_information.is_setup = None
1084
1176
  self.results.errors = None
1177
+
1178
+ # Reset execution state but preserve source file info for change detection
1179
+ self._execution_state.has_run_with_current_setup = False
1180
+ self._execution_state.has_completed_last_run = False
1181
+ self._execution_state.result_schema = None
1182
+ self._execution_state.predicted_schema = None
1183
+ self._execution_state.execution_hash = None
1184
+ # Note: source_file_info NOT reset - needed for change detection
1185
+
1085
1186
  if self.is_correct:
1086
1187
  self._schema_callback = None # Ensure the schema callback is reset
1087
1188
  if self.schema_callback:
@@ -1,9 +1,49 @@
1
1
  from collections.abc import Callable
2
2
  from dataclasses import dataclass
3
+ from enum import Enum, auto
3
4
  from typing import Literal
4
5
 
5
6
  import pyarrow as pa
6
7
 
8
+
9
+ class ExecutionStrategy(Enum):
10
+ """
11
+ Determines HOW the node will be executed.
12
+
13
+ Used by NodeExecutor to dispatch to the correct execution method.
14
+ """
15
+ SKIP = auto() # Already up-to-date, don't execute
16
+ FULL_LOCAL = auto() # 100% in-process (WASM, simple cases)
17
+ LOCAL_WITH_SAMPLING = auto() # In-process + external sampler for preview
18
+ REMOTE = auto() # Full external worker execution
19
+
20
+
21
+ class InvalidationReason(Enum):
22
+ """
23
+ Why does this node need to run?
24
+
25
+ Used for logging and debugging execution decisions.
26
+ """
27
+ NEVER_RAN = auto() # First execution
28
+ SETTINGS_CHANGED = auto() # Node configuration changed
29
+ SOURCE_FILE_CHANGED = auto() # Input file modified (read nodes)
30
+ CACHE_MISSING = auto() # Cache enabled but no cached result
31
+ FORCED_REFRESH = auto() # User requested reset_cache=True
32
+ OUTPUT_NODE = auto() # Output nodes always execute
33
+ PERFORMANCE_MODE = auto() # Running in performance mode (no caching)
34
+
35
+
36
+ @dataclass
37
+ class ExecutionDecision:
38
+ """
39
+ Result of deciding whether and how to execute a node.
40
+
41
+ Encapsulates the execution decision logic result.
42
+ """
43
+ should_run: bool
44
+ strategy: ExecutionStrategy
45
+ reason: InvalidationReason | None = None
46
+
7
47
  # Forward declaration for type hints to avoid circular imports
8
48
  if False:
9
49
  from flowfile_core.flowfile.flow_node.flow_node import FlowNode