Flowfile 0.5.4__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 (259) hide show
  1. flowfile/__main__.py +94 -1
  2. flowfile/api.py +8 -6
  3. flowfile/web/static/assets/{AdminView-f9847d67.js → AdminView-C4K1DdHI.js} +28 -33
  4. flowfile/web/static/assets/{CloudConnectionView-faace55b.js → CloudConnectionView-BZbPvPUL.js} +39 -50
  5. flowfile/web/static/assets/{CloudStorageReader-24c54524.css → CloudStorageReader-BDByiqPI.css} +25 -25
  6. flowfile/web/static/assets/{CloudStorageReader-d86ecaa7.js → CloudStorageReader-DLVukNJ7.js} +30 -35
  7. flowfile/web/static/assets/{CloudStorageWriter-0f4d9a44.js → CloudStorageWriter-Bfi-C1QW.js} +32 -37
  8. flowfile/web/static/assets/{CloudStorageWriter-60547855.css → CloudStorageWriter-y8jL8yjG.css} +24 -24
  9. flowfile/web/static/assets/{ColumnActionInput-f4189ae0.js → ColumnActionInput-BpiCApw9.js} +7 -12
  10. flowfile/web/static/assets/{ColumnSelector-e66b33da.js → ColumnSelector-CEAwedI7.js} +1 -2
  11. flowfile/web/static/assets/ContextMenu-CdojQu0w.js +9 -0
  12. flowfile/web/static/assets/ContextMenu-D12mhsy1.js +9 -0
  13. flowfile/web/static/assets/ContextMenu-EWUR98va.js +9 -0
  14. flowfile/web/static/assets/{ContextMenu.vue_vue_type_script_setup_true_lang-a1bd6314.js → ContextMenu.vue_vue_type_script_setup_true_lang-I4rXXd6G.js} +4 -5
  15. flowfile/web/static/assets/{CrossJoin-24694b8f.js → CrossJoin-BOFfxkJO.js} +19 -18
  16. flowfile/web/static/assets/{CrossJoin-71b4cc10.css → CrossJoin-Cmbyt9im.css} +18 -18
  17. flowfile/web/static/assets/{CustomNode-569d45ff.js → CustomNode-Bhpezobq.js} +12 -17
  18. flowfile/web/static/assets/{DatabaseConnectionSettings-cfc08938.js → DatabaseConnectionSettings-Dw3bSJKB.js} +10 -11
  19. flowfile/web/static/assets/{DatabaseReader-5bf8c75b.css → DatabaseReader-D6pUNUCs.css} +21 -21
  20. flowfile/web/static/assets/{DatabaseReader-701feabb.js → DatabaseReader-m87ghlw0.js} +36 -34
  21. flowfile/web/static/assets/{DatabaseView-0482e5b5.js → DatabaseView-CisSAtpe.js} +30 -38
  22. flowfile/web/static/assets/{DatabaseWriter-16721989.js → DatabaseWriter-Bbj9JLdL.js} +33 -35
  23. flowfile/web/static/assets/{DatabaseWriter-bdcf2c8b.css → DatabaseWriter-RBqdFLj8.css} +17 -17
  24. flowfile/web/static/assets/{DesignerView-f64749fb.js → DesignerView-DemDevTQ.js} +1841 -2090
  25. flowfile/web/static/assets/{DesignerView-49abb835.css → DesignerView-Dm6OzlIc.css} +244 -202
  26. flowfile/web/static/assets/{DocumentationView-61bd2990.js → DocumentationView-BrC1ZR3H.js} +3 -4
  27. flowfile/web/static/assets/{ExploreData-e2735b13.js → ExploreData-BMKcDuRb.js} +8 -10
  28. flowfile/web/static/assets/{ExternalSource-2535c3b2.js → ExternalSource-BXrNNS-f.js} +40 -42
  29. flowfile/web/static/assets/{ExternalSource-7ac7373f.css → ExternalSource-NB6WVl5R.css} +14 -14
  30. flowfile/web/static/assets/{Filter-2cdbc93c.js → Filter-C2MjsN6P.js} +36 -33
  31. flowfile/web/static/assets/{Filter-7494ea97.css → Filter-DCMGGuGC.css} +9 -9
  32. flowfile/web/static/assets/{Formula-53d58c43.css → Formula-BYafbDj8.css} +4 -4
  33. flowfile/web/static/assets/{Formula-fcda3c2c.js → Formula-ufuy4mVD.js} +27 -26
  34. flowfile/web/static/assets/{FuzzyMatch-ad6361d6.css → FuzzyMatch-BGJAwgd0.css} +42 -42
  35. flowfile/web/static/assets/{FuzzyMatch-f8d3b7d3.js → FuzzyMatch-BOHODq3h.js} +36 -38
  36. flowfile/web/static/assets/{GraphSolver-72eaa695.js → GraphSolver-B6ZzpNGO.js} +23 -21
  37. flowfile/web/static/assets/{GraphSolver-4b4d7db9.css → GraphSolver-DFN83sj3.css} +4 -4
  38. flowfile/web/static/assets/{GroupBy-8aa0598b.js → GroupBy-B9BRNcfe.js} +30 -29
  39. flowfile/web/static/assets/{Sort-4abb7fae.css → GroupBy-x4ooP5np.css} +1 -1
  40. flowfile/web/static/assets/Join-Bx_g5bZz.css +118 -0
  41. flowfile/web/static/assets/{Join-e40f0ffa.js → Join-DsBEy1IH.js} +48 -43
  42. flowfile/web/static/assets/{LoginView-5111c9ae.js → LoginView-Ct0rhdcO.js} +1 -2
  43. flowfile/web/static/assets/{ManualInput-3702e677.css → ManualInput-DlZmtMdt.css} +48 -48
  44. flowfile/web/static/assets/{ManualInput-9b6f3224.js → ManualInput-bC4BUgnG.js} +85 -44
  45. flowfile/web/static/assets/{MultiSelect-ef28e19e.js → MultiSelect-DIQ8PuTC.js} +2 -2
  46. flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-83b3bbfd.js → MultiSelect.vue_vue_type_script_setup_true_lang-BefHfqTI.js} +1 -1
  47. flowfile/web/static/assets/{NodeDesigner-d2b7ee2b.js → NodeDesigner-D39yzr2k.js} +178 -208
  48. flowfile/web/static/assets/{NodeDesigner-94cd4dd3.css → NodeDesigner-R0l6sYyY.css} +76 -76
  49. flowfile/web/static/assets/{NumericInput-1d789794.js → NumericInput-DMSX3oOr.js} +2 -2
  50. flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-7775f83e.js → NumericInput.vue_vue_type_script_setup_true_lang-d0YlVHAl.js} +1 -1
  51. flowfile/web/static/assets/{Output-cefef801.js → Output-D0VoXGcW.js} +26 -34
  52. flowfile/web/static/assets/{Output-692dd25d.css → Output-DsmglIDy.css} +5 -5
  53. flowfile/web/static/assets/{Pivot-bab1b75b.js → Pivot-BnMB4sEe.js} +26 -26
  54. flowfile/web/static/assets/{Pivot-0eda81b4.css → Pivot-qKTyWxop.css} +4 -4
  55. flowfile/web/static/assets/{PivotValidation-fba09336.js → PivotValidation-B2lWvugt.js} +7 -9
  56. flowfile/web/static/assets/{PivotValidation-e7941f91.js → PivotValidation-BPlhRjpL.js} +7 -9
  57. flowfile/web/static/assets/{PolarsCode-740e40fa.js → PolarsCode-5h0tHnWR.js} +22 -20
  58. flowfile/web/static/assets/PopOver-BHpt5rsj.js +134 -0
  59. flowfile/web/static/assets/{PopOver-d96599db.css → PopOver-CyYM4-rV.css} +1 -1
  60. flowfile/web/static/assets/{Read-90f366bc.css → Read-DJxkrTb_.css} +10 -10
  61. flowfile/web/static/assets/Read-TsLEFh3B.js +227 -0
  62. flowfile/web/static/assets/{RecordCount-ffc71eca.js → RecordCount-DkVixq9v.js} +18 -17
  63. flowfile/web/static/assets/{RecordId-a70bb8df.js → RecordId-C2UEGlCf.js} +42 -39
  64. flowfile/web/static/assets/{SQLQueryComponent-15a421f5.js → SQLQueryComponent-Dr5KMoD3.js} +2 -3
  65. flowfile/web/static/assets/{Sample-6c26afc7.js → Sample-Cb3eQNmd.js} +30 -30
  66. flowfile/web/static/assets/{SecretSelector-ceed9496.js → SecretSelector-De2L2bSx.js} +3 -4
  67. flowfile/web/static/assets/{SecretsView-214d255a.js → SecretsView-CheC9BPV.js} +13 -16
  68. flowfile/web/static/assets/{Select-8fc29999.js → Select-CI8TloRs.js} +41 -36
  69. flowfile/web/static/assets/{SettingsSection-9f0d1725.js → SettingsSection-B39ulIiI.js} +1 -2
  70. flowfile/web/static/assets/{SettingsSection-83090218.js → SettingsSection-BiCc7S9h.js} +1 -2
  71. flowfile/web/static/assets/{SettingsSection-3f70e4c3.js → SettingsSection-CITK_R7o.js} +2 -3
  72. flowfile/web/static/assets/{SettingsSection-26fe48d4.css → SettingsSection-D2GgY-Aq.css} +4 -4
  73. flowfile/web/static/assets/{SetupView-3fa0aa03.js → SetupView-C1aXRDvp.js} +3 -4
  74. flowfile/web/static/assets/{SetupView-e2da3442.css → SetupView-CI1nd-5Z.css} +38 -38
  75. flowfile/web/static/assets/{SingleSelect-a4a568cb.js → SingleSelect-Kr_hz90m.js} +2 -2
  76. flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-c8ebdd33.js → SingleSelect.vue_vue_type_script_setup_true_lang-Rxht5Z5N.js} +1 -1
  77. flowfile/web/static/assets/{SliderInput-be533e71.js → SliderInput-CLqpCxCb.js} +1 -2
  78. flowfile/web/static/assets/{GroupBy-5792782d.css → Sort-BIt2kc_p.css} +1 -1
  79. flowfile/web/static/assets/{Sort-154dad81.js → Sort-Dnw_J6Qi.js} +25 -25
  80. flowfile/web/static/assets/{TextInput-454e2bda.js → TextInput-wdlunIZC.js} +2 -2
  81. flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-e86510d0.js → TextInput.vue_vue_type_script_setup_true_lang-Bcj3ywzv.js} +1 -1
  82. flowfile/web/static/assets/{TextToRows-ea73433d.js → TextToRows-BhtyGWPq.js} +42 -49
  83. flowfile/web/static/assets/{TextToRows-12afb4f4.css → TextToRows-DivDOLDx.css} +9 -9
  84. flowfile/web/static/assets/{ToggleSwitch-9d7b30f1.js → ToggleSwitch-B-6WzfFf.js} +2 -2
  85. flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-00f2580e.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-Cj8LqT-b.js} +1 -1
  86. flowfile/web/static/assets/{UnavailableFields-b72a2c72.js → UnavailableFields-Yf6XSqFB.js} +2 -3
  87. flowfile/web/static/assets/{Union-1e44f263.js → Union-CwpjeKYC.js} +20 -23
  88. flowfile/web/static/assets/{Unpivot-b6ad6427.css → Union-DQJcpp3-.css} +6 -6
  89. flowfile/web/static/assets/{Unique-a3bc6d0a.js → Unique-25v3urqH.js} +75 -74
  90. flowfile/web/static/assets/{Union-d6a8d7d5.css → Unpivot-Deqh1gtI.css} +6 -6
  91. flowfile/web/static/assets/{Unpivot-e27935fc.js → Unpivot-sYcTTXrq.js} +34 -27
  92. flowfile/web/static/assets/{UnpivotValidation-72497680.js → UnpivotValidation-C5DDEKY2.js} +5 -7
  93. flowfile/web/static/assets/VueGraphicWalker-B8l1_Z92.js +131 -0
  94. flowfile/web/static/assets/VueGraphicWalker-Da_1-3me.css +21 -0
  95. flowfile/web/static/assets/{api-a2102880.js → api-C0LvF-0C.js} +1 -1
  96. flowfile/web/static/assets/{api-f75042b0.js → api-DaC83EO_.js} +1 -1
  97. flowfile/web/static/assets/client-C8Ygr6Gb.js +42 -0
  98. flowfile/web/static/assets/{dropDown-2798a109.js → dropDown-D5YXaPRR.js} +7 -12
  99. flowfile/web/static/assets/{fullEditor-cf7d7d93.js → fullEditor-BVYnWm05.js} +300 -18
  100. flowfile/web/static/assets/genericNodeSettings-2wAu-QKn.css +75 -0
  101. flowfile/web/static/assets/genericNodeSettings-BBtW_Cpz.js +590 -0
  102. flowfile/web/static/assets/{VueGraphicWalker-d9ab70a3.js → graphic-walker.es-VrK6vdGE.js} +92305 -89751
  103. flowfile/web/static/assets/index-BCJxPfM5.js +6693 -0
  104. flowfile/web/static/assets/{index-f0a6e5a5.js → index-CHPMUR0d.js} +150 -170
  105. flowfile/web/static/assets/index-DPkoZWq8.js +32 -0
  106. flowfile/web/static/assets/index-DnW_KC_I.js +277 -0
  107. flowfile/web/static/assets/index-UFXyfirV.css +10797 -0
  108. flowfile/web/static/assets/index-bcuE0Z0p.js +87456 -0
  109. flowfile/web/static/assets/{node.types-2c15bb7e.js → node.types-Dl4gtSW9.js} +2 -2
  110. flowfile/web/static/assets/{outputCsv-3c1757e8.js → outputCsv-BELuBiJZ.js} +2 -3
  111. flowfile/web/static/assets/outputCsv-CdGkv-fN.css +2581 -0
  112. flowfile/web/static/assets/{outputExcel-686e1f48.js → outputExcel-D0TTNM79.js} +1 -2
  113. flowfile/web/static/assets/{outputParquet-df28faa7.js → outputParquet-Cz9EbRHj.js} +1 -2
  114. flowfile/web/static/assets/{readCsv-e37eee21.js → readCsv-7bd3kUMI.js} +1 -2
  115. flowfile/web/static/assets/{readExcel-a13f14bb.js → readExcel-Cq8CCwIv.js} +3 -4
  116. flowfile/web/static/assets/{readParquet-c5244ad5.css → readParquet-CRDmBrsp.css} +4 -4
  117. flowfile/web/static/assets/{readParquet-344cf746.js → readParquet-DjR4mRaj.js} +4 -5
  118. flowfile/web/static/assets/{secrets.api-ae198c5c.js → secrets.api-C9o2KE5V.js} +1 -1
  119. flowfile/web/static/assets/{selectDynamic-6b4b0767.js → selectDynamic-Bl5FVsME.js} +5 -8
  120. flowfile/web/static/assets/useNodeSettings-dMS9zmh_.js +69 -0
  121. flowfile/web/static/assets/{vue-codemirror.esm-31ba0e0b.js → vue-codemirror.esm-CwaYwln0.js} +3469 -3064
  122. flowfile/web/static/assets/{vue-content-loader.es-4469c8ff.js → vue-content-loader.es-CMoRXo7N.js} +3 -3
  123. flowfile/web/static/index.html +2 -3
  124. {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/METADATA +2 -1
  125. flowfile-0.6.1.dist-info/RECORD +417 -0
  126. {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/WHEEL +1 -1
  127. flowfile_core/auth/password.py +1 -0
  128. flowfile_core/database/init_db.py +7 -5
  129. flowfile_core/fileExplorer/funcs.py +2 -2
  130. flowfile_core/flowfile/code_generator/code_generator.py +13 -11
  131. flowfile_core/flowfile/filter_expressions.py +327 -0
  132. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +61 -59
  133. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +3 -29
  134. flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +45 -14
  135. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +20 -3
  136. flowfile_core/flowfile/flow_data_engine/subprocess_operations/streaming.py +206 -0
  137. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +158 -24
  138. flowfile_core/flowfile/flow_graph.py +504 -190
  139. flowfile_core/flowfile/flow_node/__init__.py +32 -0
  140. flowfile_core/flowfile/flow_node/executor.py +404 -0
  141. flowfile_core/flowfile/flow_node/flow_node.py +207 -106
  142. flowfile_core/flowfile/flow_node/models.py +40 -0
  143. flowfile_core/flowfile/flow_node/output_field_config_applier.py +217 -0
  144. flowfile_core/flowfile/flow_node/schema_utils.py +78 -0
  145. flowfile_core/flowfile/flow_node/state.py +155 -0
  146. flowfile_core/flowfile/history_manager.py +401 -0
  147. flowfile_core/flowfile/manage/compatibility_enhancements.py +9 -0
  148. flowfile_core/flowfile/manage/io_flowfile.py +3 -1
  149. flowfile_core/flowfile/sources/external_sources/sql_source/models.py +20 -4
  150. flowfile_core/flowfile/util/execution_orderer.py +89 -36
  151. flowfile_core/main.py +2 -4
  152. flowfile_core/routes/auth.py +8 -9
  153. flowfile_core/routes/routes.py +320 -101
  154. flowfile_core/routes/user_defined_components.py +18 -16
  155. flowfile_core/schemas/history_schema.py +220 -0
  156. flowfile_core/schemas/input_schema.py +130 -6
  157. flowfile_core/schemas/schemas.py +9 -0
  158. flowfile_core/schemas/transform_schema.py +27 -5
  159. flowfile_core/schemas/yaml_types.py +23 -5
  160. flowfile_frame/adding_expr.py +18 -126
  161. flowfile_frame/callable_utils.py +261 -0
  162. flowfile_frame/database/connection_manager.py +0 -1
  163. flowfile_frame/expr.py +8 -4
  164. flowfile_frame/flow_frame.py +41 -41
  165. flowfile_frame/lazy.py +3 -12
  166. flowfile_frame/lazy_methods.py +5 -64
  167. flowfile_frame/utils.py +13 -32
  168. flowfile_worker/funcs.py +6 -4
  169. flowfile_worker/main.py +2 -0
  170. flowfile_worker/models.py +31 -11
  171. flowfile_worker/routes.py +60 -35
  172. flowfile_worker/spawner.py +7 -1
  173. flowfile_worker/streaming.py +335 -0
  174. flowfile/web/static/assets/ContextMenu-49463352.js +0 -9
  175. flowfile/web/static/assets/ContextMenu-dd5f3f25.js +0 -9
  176. flowfile/web/static/assets/ContextMenu-f709b884.js +0 -9
  177. flowfile/web/static/assets/Join-28b5e18f.css +0 -109
  178. flowfile/web/static/assets/PopOver-862d7e28.js +0 -939
  179. flowfile/web/static/assets/Read-225cc63f.js +0 -222
  180. flowfile/web/static/assets/VueGraphicWalker-430f0b86.css +0 -6
  181. flowfile/web/static/assets/database_reader-ce1e55f3.svg +0 -24
  182. flowfile/web/static/assets/database_writer-b4ad0753.svg +0 -23
  183. flowfile/web/static/assets/element-icons-9c88a535.woff +0 -0
  184. flowfile/web/static/assets/element-icons-de5eb258.ttf +0 -0
  185. flowfile/web/static/assets/genericNodeSettings-14eac1c3.js +0 -137
  186. flowfile/web/static/assets/genericNodeSettings-3b2507ea.css +0 -46
  187. flowfile/web/static/assets/index-387a6f18.js +0 -60752
  188. flowfile/web/static/assets/index-6b367bb5.js +0 -38
  189. flowfile/web/static/assets/index-e96ab018.css +0 -10466
  190. flowfile/web/static/assets/nodeInput-ed2ae8d7.js +0 -2
  191. flowfile/web/static/assets/outputCsv-b9a072af.css +0 -2499
  192. flowfile-0.5.4.dist-info/RECORD +0 -407
  193. /flowfile/web/static/assets/{AdminView-f53bad23.css → AdminView-B2Dthl3u.css} +0 -0
  194. /flowfile/web/static/assets/{CloudConnectionView-cf85f943.css → CloudConnectionView-BdFYGWV7.css} +0 -0
  195. /flowfile/web/static/assets/{ColumnActionInput-c44b7aee.css → ColumnActionInput-dCasSIC9.css} +0 -0
  196. /flowfile/web/static/assets/{ColumnSelector-371637fb.css → ColumnSelector-j6sEOjo1.css} +0 -0
  197. /flowfile/web/static/assets/{CustomNode-edb9b939.css → CustomNode-VPlajG0j.css} +0 -0
  198. /flowfile/web/static/assets/{DatabaseConnectionSettings-c20a1e16.css → DatabaseConnectionSettings-B78hXYgu.css} +0 -0
  199. /flowfile/web/static/assets/{DatabaseView-6655afd6.css → DatabaseView-B-_adk1s.css} +0 -0
  200. /flowfile/web/static/assets/{DocumentationView-9ea6e871.css → DocumentationView-CL7iipFL.css} +0 -0
  201. /flowfile/web/static/assets/{ExploreData-10c5acc8.css → ExploreData-DHjv0Plr.css} +0 -0
  202. /flowfile/web/static/assets/{LoginView-d325d632.css → LoginView-DN1BXY3e.css} +0 -0
  203. /flowfile/web/static/assets/{PivotValidation-0e905b1a.css → PivotValidation-DK-FARWe.css} +0 -0
  204. /flowfile/web/static/assets/{PivotValidation-41b57ad6.css → PivotValidation-FUa9F47u.css} +0 -0
  205. /flowfile/web/static/assets/{PolarsCode-2b1f1f23.css → PolarsCode-G-gRSrSc.css} +0 -0
  206. /flowfile/web/static/assets/{SQLQueryComponent-edb90b98.css → SQLQueryComponent-oAbWw0r-.css} +0 -0
  207. /flowfile/web/static/assets/{SecretSelector-6329f743.css → SecretSelector-CJSadIZx.css} +0 -0
  208. /flowfile/web/static/assets/{SecretsView-aa291340.css → SecretsView-DbzIRAba.css} +0 -0
  209. /flowfile/web/static/assets/{SettingsSection-8f980839.css → SettingsSection-BGcJnH6q.css} +0 -0
  210. /flowfile/web/static/assets/{SettingsSection-07fbbc39.css → SettingsSection-DDWn_EGW.css} +0 -0
  211. /flowfile/web/static/assets/{SliderInput-f2e4f23c.css → SliderInput-BRk-q_Dk.css} +0 -0
  212. /flowfile/web/static/assets/{UnavailableFields-394a1f78.css → UnavailableFields-DRKDImKe.css} +0 -0
  213. /flowfile/web/static/assets/{Unique-2b705521.css → Unique-Absb0aON.css} +0 -0
  214. /flowfile/web/static/assets/{UnpivotValidation-d5ca3b7b.css → UnpivotValidation-DSBkFgS-.css} +0 -0
  215. /flowfile/web/static/assets/{airbyte-292aa232.png → airbyte-W0xvIXwZ.png} +0 -0
  216. /flowfile/web/static/assets/{cloud_storage_reader-aa1415d6.png → cloud_storage_reader-3GpSCk90.png} +0 -0
  217. /flowfile/web/static/assets/{cross_join-d30c0290.png → cross_join-B0qpgYoV.png} +0 -0
  218. /flowfile/web/static/assets/{dropDown-1d6acbd9.css → dropDown-CE0VF5_P.css} +0 -0
  219. /flowfile/web/static/assets/{explore_data-8a0a2861.png → explore_data-tX6olPPL.png} +0 -0
  220. /flowfile/web/static/assets/{fa-brands-400-808443ae.ttf → fa-brands-400-D1LuMI3I.ttf} +0 -0
  221. /flowfile/web/static/assets/{fa-brands-400-d7236a19.woff2 → fa-brands-400-D_cYUPeE.woff2} +0 -0
  222. /flowfile/web/static/assets/{fa-regular-400-e3456d12.woff2 → fa-regular-400-BjRzuEpd.woff2} +0 -0
  223. /flowfile/web/static/assets/{fa-regular-400-54cf6086.ttf → fa-regular-400-DZaxPHgR.ttf} +0 -0
  224. /flowfile/web/static/assets/{fa-solid-900-aa759986.woff2 → fa-solid-900-CTAAxXor.woff2} +0 -0
  225. /flowfile/web/static/assets/{fa-solid-900-d2f05935.ttf → fa-solid-900-D0aA9rwL.ttf} +0 -0
  226. /flowfile/web/static/assets/{fa-v4compatibility-0ce9033c.woff2 → fa-v4compatibility-C9RhG_FT.woff2} +0 -0
  227. /flowfile/web/static/assets/{fa-v4compatibility-30f6abf6.ttf → fa-v4compatibility-CCth-dXg.ttf} +0 -0
  228. /flowfile/web/static/assets/{filter-d7708bda.png → filter-WRdZyUOw.png} +0 -0
  229. /flowfile/web/static/assets/{formula-eeeb1611.png → formula-CgM7uHVI.png} +0 -0
  230. /flowfile/web/static/assets/{fullEditor-fe9f7e18.css → fullEditor-CmDI7T9F.css} +0 -0
  231. /flowfile/web/static/assets/{fuzzy_match-40c161b2.png → fuzzy_match-Yon3k5Tc.png} +0 -0
  232. /flowfile/web/static/assets/{graph_solver-8b7888b8.png → graph_solver-BlMrBttD.png} +0 -0
  233. /flowfile/web/static/assets/{group_by-80561fc3.png → group_by-Gici0CSS.png} +0 -0
  234. /flowfile/web/static/assets/{input_data-ab2eb678.png → input_data-BRdGecLc.png} +0 -0
  235. /flowfile/web/static/assets/{join-349043ae.png → join-BITWRu73.png} +0 -0
  236. /flowfile/web/static/assets/{manual_input-ae98f31d.png → manual_input-CFvo_EUS.png} +0 -0
  237. /flowfile/web/static/assets/{old_join-5d0eb604.png → old_join-B9bkpPqv.png} +0 -0
  238. /flowfile/web/static/assets/{output-06ec0371.png → output-Dp7-ZpC4.png} +0 -0
  239. /flowfile/web/static/assets/{outputExcel-f5d272b2.css → outputExcel-CKgRe2iT.css} +0 -0
  240. /flowfile/web/static/assets/{outputParquet-54597c3c.css → outputParquet-d7j407cK.css} +0 -0
  241. /flowfile/web/static/assets/{pivot-9660df51.png → pivot-DSxKhNlD.png} +0 -0
  242. /flowfile/web/static/assets/{polars_code-05ce5dc6.png → polars_code-DxiztZ1c.png} +0 -0
  243. /flowfile/web/static/assets/{readCsv-3bfac4c3.css → readCsv-BG-1Jilp.css} +0 -0
  244. /flowfile/web/static/assets/{readExcel-3db6b763.css → readExcel-DBQXKPtC.css} +0 -0
  245. /flowfile/web/static/assets/{record_count-dab44eb5.png → record_count-DCeaLtpS.png} +0 -0
  246. /flowfile/web/static/assets/{record_id-0b15856b.png → record_id-FeUjyIFh.png} +0 -0
  247. /flowfile/web/static/assets/{sample-693a88b5.png → sample-DeqfRiB-.png} +0 -0
  248. /flowfile/web/static/assets/{select-b0d0437a.png → select-D4JjbdjS.png} +0 -0
  249. /flowfile/web/static/assets/{selectDynamic-f2fb394f.css → selectDynamic-CjeTPUUo.css} +0 -0
  250. /flowfile/web/static/assets/{sort-2aa579f0.png → sort-DGwUG9WS.png} +0 -0
  251. /flowfile/web/static/assets/{summarize-2a099231.png → summarize-DFaNHpfp.png} +0 -0
  252. /flowfile/web/static/assets/{text_to_rows-859b29ea.png → text_to_rows-BdiAewrN.png} +0 -0
  253. /flowfile/web/static/assets/{union-2d8609f4.png → union-DCK-LSMq.png} +0 -0
  254. /flowfile/web/static/assets/{unique-1958b98a.png → unique-CdP3zZIq.png} +0 -0
  255. /flowfile/web/static/assets/{unpivot-d3cb4b5b.png → unpivot-CHttrEt8.png} +0 -0
  256. /flowfile/web/static/assets/{user-defined-icon-0ae16c90.png → user-defined-icon-BcIp2Vzo.png} +0 -0
  257. /flowfile/web/static/assets/{view-7a0f0be1.png → view-DUSRwjvq.png} +0 -0
  258. {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/entry_points.txt +0 -0
  259. {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,206 @@
1
+ """
2
+ WebSocket streaming client for core-to-worker communication.
3
+
4
+ Replaces the HTTP poll-based pattern with a single WebSocket connection:
5
+ - Sends task metadata + serialized LazyFrame as binary
6
+ - Receives progress updates as JSON pushes
7
+ - Receives result as raw binary frame (no base64 encoding)
8
+
9
+ Falls back to REST automatically if the worker doesn't support WebSocket.
10
+ """
11
+
12
+ import io
13
+ import json
14
+ from base64 import b64encode
15
+ from typing import Any
16
+
17
+ import polars as pl
18
+ from websockets.sync.client import connect
19
+
20
+ from flowfile_core.configs.settings import WORKER_URL
21
+ from flowfile_core.flowfile.flow_data_engine.subprocess_operations.models import Status
22
+
23
+
24
+ def _get_ws_url() -> str:
25
+ """Convert HTTP worker URL to WebSocket URL."""
26
+ return WORKER_URL.replace("http://", "ws://").replace("https://", "wss://")
27
+
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Message building
31
+ # ---------------------------------------------------------------------------
32
+
33
+ def _build_metadata(
34
+ task_id: str,
35
+ operation_type: str,
36
+ flow_id: int,
37
+ node_id: int | str,
38
+ kwargs: dict | None,
39
+ ) -> dict:
40
+ """Build the JSON metadata message for the WebSocket protocol."""
41
+ metadata = {
42
+ "task_id": task_id,
43
+ "operation": operation_type,
44
+ "flow_id": flow_id,
45
+ "node_id": node_id,
46
+ }
47
+ if kwargs:
48
+ metadata["kwargs"] = kwargs
49
+ return metadata
50
+
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # Message receiving
54
+ # ---------------------------------------------------------------------------
55
+
56
+ def _handle_complete_message(data: dict, task_id: str) -> Status:
57
+ """Build a partial Status from a 'complete' protocol message.
58
+
59
+ The ``results`` field is set to None here and populated by the caller
60
+ once the actual result payload has been received.
61
+ """
62
+ return Status(
63
+ background_task_id=task_id,
64
+ status="Completed",
65
+ file_ref=data.get("file_ref", ""),
66
+ result_type=data.get("result_type", "polars"),
67
+ progress=100,
68
+ results=data.get("results", None),
69
+ )
70
+
71
+
72
+ def _receive_raw_result(ws, task_id: str) -> tuple[Any, Status | None]:
73
+ """Receive messages from the worker until a raw result or error arrives.
74
+
75
+ Returns the result **without** deserializing it so the caller can both
76
+ populate ``Status.results`` (b64-encoded bytes for polars) and
77
+ deserialize into the in-memory object.
78
+
79
+ Returns:
80
+ (raw_result, status) where raw_result is ``bytes`` for polars
81
+ results, arbitrary data for "other" results, or ``None``.
82
+ """
83
+ raw_result = None
84
+ status = None
85
+
86
+ while True:
87
+ msg = ws.recv()
88
+
89
+ if isinstance(msg, bytes):
90
+ raw_result = msg
91
+ break
92
+
93
+ data = json.loads(msg)
94
+ msg_type = data.get("type")
95
+
96
+ if msg_type == "progress":
97
+ continue
98
+
99
+ if msg_type == "complete":
100
+ status = _handle_complete_message(data, task_id)
101
+ if not data.get("has_result", False):
102
+ break
103
+ continue
104
+
105
+ if msg_type == "result_data":
106
+ raw_result = data.get("data")
107
+ break
108
+
109
+ if msg_type == "error":
110
+ raise Exception(data.get("error_message", "Unknown worker error"))
111
+
112
+ return raw_result, status
113
+
114
+
115
+ def _deserialize_and_populate_status(
116
+ raw_result: Any, status: Status
117
+ ) -> tuple[Any, Status]:
118
+ """Deserialize the raw result and fill ``status.results``.
119
+
120
+ For polars results (bytes): deserializes into a LazyFrame and stores
121
+ the b64-encoded bytes in ``status.results`` (matching REST behaviour).
122
+ For other results: stores the value directly in ``status.results``.
123
+ """
124
+ if raw_result is None:
125
+ return None, status
126
+
127
+ if isinstance(raw_result, bytes):
128
+ status.results = b64encode(raw_result).decode("ascii")
129
+ return pl.LazyFrame.deserialize(io.BytesIO(raw_result)), status
130
+
131
+ status.results = raw_result
132
+ return raw_result, status
133
+
134
+
135
+ def streaming_start(
136
+ task_id: str,
137
+ operation_type: str,
138
+ flow_id: int,
139
+ node_id: int | str,
140
+ lf_bytes: bytes,
141
+ kwargs: dict | None = None,
142
+ ):
143
+ """Open a WebSocket connection and send the task.
144
+
145
+ Returns the **open** connection. The caller must eventually call
146
+ :func:`streaming_receive` (which closes the connection) or close it
147
+ manually.
148
+
149
+ Raises immediately on connection failure or send error.
150
+ """
151
+ ws_url = _get_ws_url() + "/ws/submit"
152
+ metadata = _build_metadata(task_id, operation_type, flow_id, node_id, kwargs)
153
+
154
+ ws = connect(ws_url)
155
+ try:
156
+ ws.send(json.dumps(metadata))
157
+ ws.send(lf_bytes)
158
+ except Exception:
159
+ ws.close()
160
+ raise
161
+ return ws
162
+
163
+
164
+ def streaming_receive(ws, task_id: str) -> tuple[Any, Status]:
165
+ """Block until the worker sends back a result, then close the connection.
166
+
167
+ The returned ``Status`` object is fully populated (including
168
+ ``results``) so it is equivalent to what the REST polling path
169
+ would produce.
170
+
171
+ Returns:
172
+ Tuple of (result, Status)
173
+ """
174
+ try:
175
+ raw_result, status = _receive_raw_result(ws, task_id)
176
+ finally:
177
+ ws.close()
178
+
179
+ if status is None:
180
+ status = Status(
181
+ background_task_id=task_id,
182
+ status="Completed",
183
+ file_ref="",
184
+ result_type="polars",
185
+ progress=100,
186
+ results=None,
187
+ )
188
+
189
+ return _deserialize_and_populate_status(raw_result, status)
190
+
191
+
192
+ def streaming_submit(
193
+ task_id: str,
194
+ operation_type: str,
195
+ flow_id: int,
196
+ node_id: int | str,
197
+ lf_bytes: bytes,
198
+ kwargs: dict | None = None,
199
+ ) -> tuple[Any, Status]:
200
+ """Submit a task via WebSocket and block until the result arrives.
201
+
202
+ Convenience wrapper around :func:`streaming_start` +
203
+ :func:`streaming_receive`.
204
+ """
205
+ ws = streaming_start(task_id, operation_type, flow_id, node_id, lf_bytes, kwargs)
206
+ return streaming_receive(ws, task_id)
@@ -1,7 +1,7 @@
1
1
  # Standard library imports
2
2
  import io
3
3
  import threading
4
- from base64 import decodebytes, encodebytes
4
+ from base64 import b64decode
5
5
  from time import sleep
6
6
  from typing import Any, Literal
7
7
  from uuid import uuid4
@@ -18,6 +18,11 @@ from flowfile_core.flowfile.flow_data_engine.subprocess_operations.models import
18
18
  PolarsOperation,
19
19
  Status,
20
20
  )
21
+ from flowfile_core.flowfile.flow_data_engine.subprocess_operations.streaming import (
22
+ streaming_receive,
23
+ streaming_start,
24
+ streaming_submit,
25
+ )
21
26
  from flowfile_core.flowfile.sources.external_sources.sql_source.models import (
22
27
  DatabaseExternalReadSettings,
23
28
  DatabaseExternalWriteSettings,
@@ -30,15 +35,15 @@ from flowfile_core.utils.arrow_reader import read
30
35
  def trigger_df_operation(
31
36
  flow_id: int, node_id: int | str, lf: pl.LazyFrame, file_ref: str, operation_type: OperationType = "store"
32
37
  ) -> Status:
33
- encoded_operation = encodebytes(lf.serialize()).decode()
34
- _json = {
35
- "task_id": file_ref,
36
- "operation": encoded_operation,
37
- "operation_type": operation_type,
38
- "flowfile_flow_id": flow_id,
39
- "flowfile_node_id": node_id,
38
+ # Send raw bytes directly - no base64 encoding overhead
39
+ headers = {
40
+ "Content-Type": "application/octet-stream",
41
+ "X-Task-Id": file_ref,
42
+ "X-Operation-Type": operation_type,
43
+ "X-Flow-Id": str(flow_id),
44
+ "X-Node-Id": str(node_id),
40
45
  }
41
- v = requests.post(url=f"{WORKER_URL}/submit_query/", json=_json)
46
+ v = requests.post(url=f"{WORKER_URL}/submit_query/", data=lf.serialize(), headers=headers)
42
47
  if not v.ok:
43
48
  raise Exception(f"trigger_df_operation: Could not cache the data, {v.text}")
44
49
  return Status(**v.json())
@@ -47,16 +52,16 @@ def trigger_df_operation(
47
52
  def trigger_sample_operation(
48
53
  lf: pl.LazyFrame, file_ref: str, flow_id: int, node_id: str | int, sample_size: int = 100
49
54
  ) -> Status:
50
- encoded_operation = encodebytes(lf.serialize()).decode()
51
- _json = {
52
- "task_id": file_ref,
53
- "operation": encoded_operation,
54
- "operation_type": "store_sample",
55
- "sample_size": sample_size,
56
- "flowfile_flow_id": flow_id,
57
- "flowfile_node_id": node_id,
55
+ # Send raw bytes directly - no base64 encoding overhead
56
+ headers = {
57
+ "Content-Type": "application/octet-stream",
58
+ "X-Task-Id": file_ref,
59
+ "X-Operation-Type": "store_sample",
60
+ "X-Sample-Size": str(sample_size),
61
+ "X-Flow-Id": str(flow_id),
62
+ "X-Node-Id": str(node_id),
58
63
  }
59
- v = requests.post(url=f"{WORKER_URL}/store_sample/", json=_json)
64
+ v = requests.post(url=f"{WORKER_URL}/store_sample/", data=lf.serialize(), headers=headers)
60
65
  if not v.ok:
61
66
  raise Exception(f"trigger_sample_operation: Could not cache the data, {v.text}")
62
67
  return Status(**v.json())
@@ -70,8 +75,9 @@ def trigger_fuzzy_match_operation(
70
75
  flow_id: int,
71
76
  node_id: int | str,
72
77
  ) -> Status:
73
- left_serializable_object = PolarsOperation(operation=encodebytes(left_df.serialize()))
74
- right_serializable_object = PolarsOperation(operation=encodebytes(right_df.serialize()))
78
+ # Use raw bytes - Pydantic will handle single base64 encoding for JSON transport
79
+ left_serializable_object = PolarsOperation(operation=left_df.serialize())
80
+ right_serializable_object = PolarsOperation(operation=right_df.serialize())
75
81
  fuzzy_join_input = FuzzyJoinInput(
76
82
  left_df_operation=left_serializable_object,
77
83
  right_df_operation=right_serializable_object,
@@ -80,7 +86,6 @@ def trigger_fuzzy_match_operation(
80
86
  flowfile_flow_id=flow_id,
81
87
  flowfile_node_id=node_id,
82
88
  )
83
- print("fuzzy join input", fuzzy_join_input)
84
89
  v = requests.post(f"{WORKER_URL}/add_fuzzy_join", data=fuzzy_join_input.model_dump_json())
85
90
  if not v.ok:
86
91
  raise Exception(f"trigger_fuzzy_match_operation: Could not cache the data, {v.text}")
@@ -137,6 +142,12 @@ def get_results(file_ref: str) -> Status | None:
137
142
 
138
143
 
139
144
  def results_exists(file_ref: str):
145
+ from flowfile_core.configs.settings import OFFLOAD_TO_WORKER
146
+
147
+ # Skip worker check if worker communication is disabled
148
+ if not OFFLOAD_TO_WORKER:
149
+ return False
150
+
140
151
  try:
141
152
  f = requests.get(f"{WORKER_URL}/status/{file_ref}")
142
153
  if f.status_code == 200:
@@ -159,6 +170,12 @@ def clear_task_from_worker(file_ref: str) -> bool:
159
170
  Returns:
160
171
  bool: True if the task was successfully cleared, False otherwise.
161
172
  """
173
+ from flowfile_core.configs.settings import OFFLOAD_TO_WORKER
174
+
175
+ # Skip worker call if worker communication is disabled
176
+ if not OFFLOAD_TO_WORKER:
177
+ return False
178
+
162
179
  try:
163
180
  f = requests.delete(f"{WORKER_URL}/clear_task/{file_ref}")
164
181
  if f.status_code == 200:
@@ -169,9 +186,9 @@ def clear_task_from_worker(file_ref: str) -> bool:
169
186
  return False
170
187
 
171
188
 
172
- def get_df_result(encoded_df: str) -> pl.LazyFrame:
173
- r = decodebytes(encoded_df.encode())
174
- return pl.LazyFrame.deserialize(io.BytesIO(r))
189
+ def get_df_result(result_b64: str) -> pl.LazyFrame:
190
+ # Results are base64-encoded string from JSON response, decode once
191
+ return pl.LazyFrame.deserialize(io.BytesIO(b64decode(result_b64)))
175
192
 
176
193
 
177
194
  def get_external_df_result(file_ref: str) -> pl.LazyFrame | None:
@@ -228,6 +245,9 @@ class BaseFetcher:
228
245
  self._stop_event = threading.Event()
229
246
  self._thread = None
230
247
 
248
+ # WebSocket connection for non-blocking streaming mode
249
+ self._ws = None
250
+
231
251
  # State variables - use properties for thread-safe access
232
252
  self._result: Any | None = None
233
253
  self._started: bool = False
@@ -378,6 +398,16 @@ class BaseFetcher:
378
398
  """
379
399
  logger.warning("Cancelling the operation")
380
400
 
401
+ # Close WebSocket if streaming (causes recv thread to exit)
402
+ with self._lock:
403
+ ws = self._ws
404
+ self._ws = None
405
+ if ws is not None:
406
+ try:
407
+ ws.close()
408
+ except Exception:
409
+ pass
410
+
381
411
  # Cancel on the worker side
382
412
  try:
383
413
  cancel_task(self.file_ref)
@@ -439,6 +469,79 @@ class BaseFetcher:
439
469
  with self._lock:
440
470
  return self._error_code, self._error_description
441
471
 
472
+ def _execute_streaming(
473
+ self,
474
+ operation_type: str,
475
+ flow_id: int,
476
+ node_id: int | str,
477
+ lf_bytes: bytes,
478
+ kwargs: dict | None = None,
479
+ blocking: bool = True,
480
+ ) -> None:
481
+ """Execute via WebSocket streaming - no polling, binary result transfer.
482
+
483
+ Args:
484
+ blocking: If True (default), blocks until the result is available
485
+ and sets self._result directly. If False, opens the
486
+ connection, sends the task, and hands off to a background
487
+ thread that will set self._result when done.
488
+
489
+ Raises on connection or send error so the caller can fall back to REST.
490
+ """
491
+ if blocking:
492
+ result, status = streaming_submit(
493
+ task_id=self.file_ref,
494
+ operation_type=operation_type,
495
+ flow_id=flow_id,
496
+ node_id=node_id,
497
+ lf_bytes=lf_bytes,
498
+ kwargs=kwargs,
499
+ )
500
+ with self._lock:
501
+ self._result = result
502
+ self._running = False
503
+ self._started = True
504
+ self.status = status
505
+ else:
506
+ # Non-blocking: open connection, send task, receive in background
507
+ ws = streaming_start(
508
+ task_id=self.file_ref,
509
+ operation_type=operation_type,
510
+ flow_id=flow_id,
511
+ node_id=node_id,
512
+ lf_bytes=lf_bytes,
513
+ kwargs=kwargs,
514
+ )
515
+ with self._lock:
516
+ self._ws = ws
517
+ self._running = True
518
+ self._started = True
519
+ self._thread = threading.Thread(
520
+ target=self._ws_receive_thread,
521
+ args=(ws,),
522
+ daemon=True,
523
+ )
524
+ self._thread.start()
525
+
526
+ def _ws_receive_thread(self, ws) -> None:
527
+ """Background thread that receives results over an open WebSocket."""
528
+ try:
529
+ result, status = streaming_receive(ws, self.file_ref)
530
+ with self._condition:
531
+ self._result = result
532
+ self._running = False
533
+ self._ws = None
534
+ self._condition.notify_all()
535
+ self.status = status
536
+ except Exception as e:
537
+ logger.exception("Error in WebSocket receive thread")
538
+ with self._condition:
539
+ self._error_code = -1
540
+ self._error_description = str(e)
541
+ self._running = False
542
+ self._ws = None
543
+ self._condition.notify_all()
544
+
442
545
 
443
546
  class ExternalDfFetcher(BaseFetcher):
444
547
  status: Status | None = None
@@ -455,6 +558,21 @@ class ExternalDfFetcher(BaseFetcher):
455
558
  ):
456
559
  super().__init__(file_ref=file_ref)
457
560
  lf = lf.lazy() if isinstance(lf, pl.DataFrame) else lf
561
+
562
+ # Try WebSocket streaming first (blocking or non-blocking)
563
+ try:
564
+ self._execute_streaming(
565
+ operation_type=operation_type,
566
+ flow_id=flow_id,
567
+ node_id=node_id,
568
+ lf_bytes=lf.serialize(),
569
+ blocking=wait_on_completion,
570
+ )
571
+ return
572
+ except Exception as e:
573
+ logger.debug(f"WebSocket streaming unavailable ({e}), falling back to REST")
574
+
575
+ # REST fallback (original behavior)
458
576
  r = trigger_df_operation(
459
577
  lf=lf, file_ref=self.file_ref, operation_type=operation_type, node_id=node_id, flow_id=flow_id
460
578
  )
@@ -478,6 +596,22 @@ class ExternalSampler(BaseFetcher):
478
596
  ):
479
597
  super().__init__(file_ref=file_ref)
480
598
  lf = lf.lazy() if isinstance(lf, pl.DataFrame) else lf
599
+
600
+ # Try WebSocket streaming first (blocking or non-blocking)
601
+ try:
602
+ self._execute_streaming(
603
+ operation_type="store_sample",
604
+ flow_id=flow_id,
605
+ node_id=node_id,
606
+ lf_bytes=lf.serialize(),
607
+ kwargs={"sample_size": sample_size},
608
+ blocking=wait_on_completion,
609
+ )
610
+ return
611
+ except Exception as e:
612
+ logger.debug(f"WebSocket streaming unavailable ({e}), falling back to REST")
613
+
614
+ # REST fallback (original behavior)
481
615
  r = trigger_sample_operation(
482
616
  lf=lf, file_ref=file_ref, sample_size=sample_size, node_id=node_id, flow_id=flow_id
483
617
  )