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
@@ -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}")
@@ -181,9 +186,9 @@ def clear_task_from_worker(file_ref: str) -> bool:
181
186
  return False
182
187
 
183
188
 
184
- def get_df_result(encoded_df: str) -> pl.LazyFrame:
185
- r = decodebytes(encoded_df.encode())
186
- 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)))
187
192
 
188
193
 
189
194
  def get_external_df_result(file_ref: str) -> pl.LazyFrame | None:
@@ -240,6 +245,9 @@ class BaseFetcher:
240
245
  self._stop_event = threading.Event()
241
246
  self._thread = None
242
247
 
248
+ # WebSocket connection for non-blocking streaming mode
249
+ self._ws = None
250
+
243
251
  # State variables - use properties for thread-safe access
244
252
  self._result: Any | None = None
245
253
  self._started: bool = False
@@ -390,6 +398,16 @@ class BaseFetcher:
390
398
  """
391
399
  logger.warning("Cancelling the operation")
392
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
+
393
411
  # Cancel on the worker side
394
412
  try:
395
413
  cancel_task(self.file_ref)
@@ -451,6 +469,79 @@ class BaseFetcher:
451
469
  with self._lock:
452
470
  return self._error_code, self._error_description
453
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
+
454
545
 
455
546
  class ExternalDfFetcher(BaseFetcher):
456
547
  status: Status | None = None
@@ -467,6 +558,21 @@ class ExternalDfFetcher(BaseFetcher):
467
558
  ):
468
559
  super().__init__(file_ref=file_ref)
469
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)
470
576
  r = trigger_df_operation(
471
577
  lf=lf, file_ref=self.file_ref, operation_type=operation_type, node_id=node_id, flow_id=flow_id
472
578
  )
@@ -490,6 +596,22 @@ class ExternalSampler(BaseFetcher):
490
596
  ):
491
597
  super().__init__(file_ref=file_ref)
492
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)
493
615
  r = trigger_sample_operation(
494
616
  lf=lf, file_ref=file_ref, sample_size=sample_size, node_id=node_id, flow_id=flow_id
495
617
  )