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.
- flowfile/api.py +8 -6
- flowfile/web/static/assets/{AdminView-c2c7942b.js → AdminView-C4K1DdHI.js} +28 -33
- flowfile/web/static/assets/{CloudConnectionView-7a3042c6.js → CloudConnectionView-BZbPvPUL.js} +39 -50
- flowfile/web/static/assets/{CloudStorageReader-24c54524.css → CloudStorageReader-BDByiqPI.css} +25 -25
- flowfile/web/static/assets/{CloudStorageReader-709c4037.js → CloudStorageReader-DLVukNJ7.js} +30 -35
- flowfile/web/static/assets/{CloudStorageWriter-604c51a8.js → CloudStorageWriter-Bfi-C1QW.js} +32 -37
- flowfile/web/static/assets/{CloudStorageWriter-60547855.css → CloudStorageWriter-y8jL8yjG.css} +24 -24
- flowfile/web/static/assets/{ColumnActionInput-d63d6746.js → ColumnActionInput-BpiCApw9.js} +7 -12
- flowfile/web/static/assets/{ColumnSelector-0c8cd1cd.js → ColumnSelector-CEAwedI7.js} +1 -2
- flowfile/web/static/assets/ContextMenu-CdojQu0w.js +9 -0
- flowfile/web/static/assets/ContextMenu-D12mhsy1.js +9 -0
- flowfile/web/static/assets/ContextMenu-EWUR98va.js +9 -0
- 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
- flowfile/web/static/assets/{CrossJoin-38e5b99a.js → CrossJoin-BOFfxkJO.js} +19 -18
- flowfile/web/static/assets/{CrossJoin-71b4cc10.css → CrossJoin-Cmbyt9im.css} +18 -18
- flowfile/web/static/assets/{CustomNode-76e8f3f5.js → CustomNode-Bhpezobq.js} +12 -17
- flowfile/web/static/assets/{DatabaseConnectionSettings-38155669.js → DatabaseConnectionSettings-Dw3bSJKB.js} +10 -11
- flowfile/web/static/assets/{DatabaseReader-5bf8c75b.css → DatabaseReader-D6pUNUCs.css} +21 -21
- flowfile/web/static/assets/{DatabaseReader-2e549c8f.js → DatabaseReader-m87ghlw0.js} +36 -34
- flowfile/web/static/assets/{DatabaseView-dc877c29.js → DatabaseView-CisSAtpe.js} +30 -38
- flowfile/web/static/assets/{DatabaseWriter-ffb91864.js → DatabaseWriter-Bbj9JLdL.js} +33 -35
- flowfile/web/static/assets/{DatabaseWriter-bdcf2c8b.css → DatabaseWriter-RBqdFLj8.css} +17 -17
- flowfile/web/static/assets/{DesignerView-a4466dab.js → DesignerView-DemDevTQ.js} +1752 -2054
- flowfile/web/static/assets/{DesignerView-71d4e9a1.css → DesignerView-Dm6OzlIc.css} +209 -168
- flowfile/web/static/assets/{DocumentationView-979afc84.js → DocumentationView-BrC1ZR3H.js} +3 -4
- flowfile/web/static/assets/{ExploreData-e4b92aaf.js → ExploreData-BMKcDuRb.js} +8 -10
- flowfile/web/static/assets/{ExternalSource-d08e7227.js → ExternalSource-BXrNNS-f.js} +40 -42
- flowfile/web/static/assets/{ExternalSource-7ac7373f.css → ExternalSource-NB6WVl5R.css} +14 -14
- flowfile/web/static/assets/{Filter-7add806d.js → Filter-C2MjsN6P.js} +36 -33
- flowfile/web/static/assets/{Filter-7494ea97.css → Filter-DCMGGuGC.css} +9 -9
- flowfile/web/static/assets/{Formula-53d58c43.css → Formula-BYafbDj8.css} +4 -4
- flowfile/web/static/assets/{Formula-36ab24d2.js → Formula-ufuy4mVD.js} +27 -26
- flowfile/web/static/assets/{FuzzyMatch-ad6361d6.css → FuzzyMatch-BGJAwgd0.css} +42 -42
- flowfile/web/static/assets/{FuzzyMatch-cc01bb04.js → FuzzyMatch-BOHODq3h.js} +36 -38
- flowfile/web/static/assets/{GraphSolver-4fb98f3b.js → GraphSolver-B6ZzpNGO.js} +23 -21
- flowfile/web/static/assets/{GraphSolver-4b4d7db9.css → GraphSolver-DFN83sj3.css} +4 -4
- flowfile/web/static/assets/{GroupBy-b3c8f429.js → GroupBy-B9BRNcfe.js} +30 -29
- flowfile/web/static/assets/{Sort-4abb7fae.css → GroupBy-x4ooP5np.css} +1 -1
- flowfile/web/static/assets/Join-Bx_g5bZz.css +118 -0
- flowfile/web/static/assets/{Join-096b7b26.js → Join-DsBEy1IH.js} +48 -43
- flowfile/web/static/assets/{LoginView-c33a246a.js → LoginView-Ct0rhdcO.js} +1 -2
- flowfile/web/static/assets/{ManualInput-39111f19.css → ManualInput-DlZmtMdt.css} +48 -48
- flowfile/web/static/assets/{ManualInput-7307e9b1.js → ManualInput-bC4BUgnG.js} +40 -41
- flowfile/web/static/assets/{MultiSelect-14822c48.js → MultiSelect-DIQ8PuTC.js} +2 -2
- 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
- flowfile/web/static/assets/{NodeDesigner-5036c392.js → NodeDesigner-D39yzr2k.js} +178 -208
- flowfile/web/static/assets/{NodeDesigner-94cd4dd3.css → NodeDesigner-R0l6sYyY.css} +76 -76
- flowfile/web/static/assets/{NumericInput-15cf3b72.js → NumericInput-DMSX3oOr.js} +2 -2
- 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
- flowfile/web/static/assets/{Output-1f8ed42c.js → Output-D0VoXGcW.js} +26 -34
- flowfile/web/static/assets/{Output-692dd25d.css → Output-DsmglIDy.css} +5 -5
- flowfile/web/static/assets/{Pivot-0e153f4e.js → Pivot-BnMB4sEe.js} +26 -26
- flowfile/web/static/assets/{Pivot-0eda81b4.css → Pivot-qKTyWxop.css} +4 -4
- flowfile/web/static/assets/{PivotValidation-81ec2a33.js → PivotValidation-B2lWvugt.js} +7 -9
- flowfile/web/static/assets/{PivotValidation-5a4f7c79.js → PivotValidation-BPlhRjpL.js} +7 -9
- flowfile/web/static/assets/{PolarsCode-a39f15ac.js → PolarsCode-5h0tHnWR.js} +22 -20
- flowfile/web/static/assets/{PopOver-ddcfe4f6.js → PopOver-BHpt5rsj.js} +5 -9
- flowfile/web/static/assets/{PopOver-d96599db.css → PopOver-CyYM4-rV.css} +1 -1
- flowfile/web/static/assets/{Read-90f366bc.css → Read-DJxkrTb_.css} +10 -10
- flowfile/web/static/assets/Read-TsLEFh3B.js +227 -0
- flowfile/web/static/assets/{RecordCount-e9048ccd.js → RecordCount-DkVixq9v.js} +18 -17
- flowfile/web/static/assets/{RecordId-ad02521d.js → RecordId-C2UEGlCf.js} +42 -39
- flowfile/web/static/assets/{SQLQueryComponent-2eeecf0b.js → SQLQueryComponent-Dr5KMoD3.js} +2 -3
- flowfile/web/static/assets/{Sample-9a68c23d.js → Sample-Cb3eQNmd.js} +30 -30
- flowfile/web/static/assets/{SecretSelector-2429f35a.js → SecretSelector-De2L2bSx.js} +3 -4
- flowfile/web/static/assets/{SecretsView-c6afc915.js → SecretsView-CheC9BPV.js} +13 -16
- flowfile/web/static/assets/{Select-fcd002b6.js → Select-CI8TloRs.js} +41 -36
- flowfile/web/static/assets/{SettingsSection-5ce15962.js → SettingsSection-B39ulIiI.js} +1 -2
- flowfile/web/static/assets/{SettingsSection-c6b1362c.js → SettingsSection-BiCc7S9h.js} +1 -2
- flowfile/web/static/assets/{SettingsSection-cebb91d5.js → SettingsSection-CITK_R7o.js} +2 -3
- flowfile/web/static/assets/{SettingsSection-26fe48d4.css → SettingsSection-D2GgY-Aq.css} +4 -4
- flowfile/web/static/assets/{SetupView-2d12e01f.js → SetupView-C1aXRDvp.js} +1 -2
- flowfile/web/static/assets/{SingleSelect-b67de4eb.js → SingleSelect-Kr_hz90m.js} +2 -2
- 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
- flowfile/web/static/assets/{SliderInput-fd8134ac.js → SliderInput-CLqpCxCb.js} +1 -2
- flowfile/web/static/assets/{GroupBy-5792782d.css → Sort-BIt2kc_p.css} +1 -1
- flowfile/web/static/assets/{Sort-c005a573.js → Sort-Dnw_J6Qi.js} +25 -25
- flowfile/web/static/assets/{TextInput-1bb31dab.js → TextInput-wdlunIZC.js} +2 -2
- 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
- flowfile/web/static/assets/{TextToRows-4f363753.js → TextToRows-BhtyGWPq.js} +42 -49
- flowfile/web/static/assets/{TextToRows-12afb4f4.css → TextToRows-DivDOLDx.css} +9 -9
- flowfile/web/static/assets/{ToggleSwitch-ca0f2e5e.js → ToggleSwitch-B-6WzfFf.js} +2 -2
- 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
- flowfile/web/static/assets/{UnavailableFields-f6147968.js → UnavailableFields-Yf6XSqFB.js} +2 -3
- flowfile/web/static/assets/{Union-c65f17b7.js → Union-CwpjeKYC.js} +20 -23
- flowfile/web/static/assets/{Unpivot-b6ad6427.css → Union-DQJcpp3-.css} +6 -6
- flowfile/web/static/assets/{Unique-a1d96fb2.js → Unique-25v3urqH.js} +75 -74
- flowfile/web/static/assets/{Union-d6a8d7d5.css → Unpivot-Deqh1gtI.css} +6 -6
- flowfile/web/static/assets/{Unpivot-c2657ff3.js → Unpivot-sYcTTXrq.js} +34 -27
- flowfile/web/static/assets/{UnpivotValidation-28e29a3b.js → UnpivotValidation-C5DDEKY2.js} +5 -7
- flowfile/web/static/assets/VueGraphicWalker-B8l1_Z92.js +131 -0
- flowfile/web/static/assets/VueGraphicWalker-Da_1-3me.css +21 -0
- flowfile/web/static/assets/{api-df48ec50.js → api-C0LvF-0C.js} +1 -1
- flowfile/web/static/assets/{api-ee542cf7.js → api-DaC83EO_.js} +1 -1
- flowfile/web/static/assets/client-C8Ygr6Gb.js +42 -0
- flowfile/web/static/assets/{dropDown-7576a76a.js → dropDown-D5YXaPRR.js} +7 -12
- flowfile/web/static/assets/{fullEditor-7583bef5.js → fullEditor-BVYnWm05.js} +300 -18
- flowfile/web/static/assets/genericNodeSettings-2wAu-QKn.css +75 -0
- flowfile/web/static/assets/genericNodeSettings-BBtW_Cpz.js +590 -0
- flowfile/web/static/assets/{VueGraphicWalker-2fc3ddd4.js → graphic-walker.es-VrK6vdGE.js} +92305 -89751
- flowfile/web/static/assets/index-BCJxPfM5.js +6693 -0
- flowfile/web/static/assets/{index-057d770d.js → index-CHPMUR0d.js} +150 -170
- flowfile/web/static/assets/index-DPkoZWq8.js +32 -0
- flowfile/web/static/assets/index-DnW_KC_I.js +277 -0
- flowfile/web/static/assets/index-UFXyfirV.css +10797 -0
- flowfile/web/static/assets/index-bcuE0Z0p.js +87456 -0
- flowfile/web/static/assets/{node.types-2c15bb7e.js → node.types-Dl4gtSW9.js} +2 -2
- flowfile/web/static/assets/{outputCsv-c492b15e.js → outputCsv-BELuBiJZ.js} +1 -2
- flowfile/web/static/assets/outputCsv-CdGkv-fN.css +2581 -0
- flowfile/web/static/assets/{outputExcel-13bfa10f.js → outputExcel-D0TTNM79.js} +1 -2
- flowfile/web/static/assets/{outputParquet-9be1523a.js → outputParquet-Cz9EbRHj.js} +1 -2
- flowfile/web/static/assets/{readCsv-5a49a8c9.js → readCsv-7bd3kUMI.js} +1 -2
- flowfile/web/static/assets/{readExcel-27c30ad8.js → readExcel-Cq8CCwIv.js} +3 -4
- flowfile/web/static/assets/{readParquet-c5244ad5.css → readParquet-CRDmBrsp.css} +4 -4
- flowfile/web/static/assets/{readParquet-446bde68.js → readParquet-DjR4mRaj.js} +4 -5
- flowfile/web/static/assets/{secrets.api-34431884.js → secrets.api-C9o2KE5V.js} +1 -1
- flowfile/web/static/assets/{selectDynamic-5754a2b1.js → selectDynamic-Bl5FVsME.js} +5 -7
- flowfile/web/static/assets/useNodeSettings-dMS9zmh_.js +69 -0
- flowfile/web/static/assets/{vue-codemirror.esm-8f46fb36.js → vue-codemirror.esm-CwaYwln0.js} +3469 -3064
- flowfile/web/static/assets/{vue-content-loader.es-808fe33a.js → vue-content-loader.es-CMoRXo7N.js} +3 -3
- flowfile/web/static/index.html +2 -3
- {flowfile-0.5.6.dist-info → flowfile-0.6.1.dist-info}/METADATA +2 -1
- flowfile-0.6.1.dist-info/RECORD +417 -0
- {flowfile-0.5.6.dist-info → flowfile-0.6.1.dist-info}/WHEEL +1 -1
- flowfile_core/auth/password.py +1 -0
- flowfile_core/database/init_db.py +7 -5
- flowfile_core/fileExplorer/funcs.py +2 -2
- flowfile_core/flowfile/code_generator/code_generator.py +13 -11
- flowfile_core/flowfile/filter_expressions.py +327 -0
- flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +61 -59
- flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +3 -29
- flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +45 -14
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +20 -3
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/streaming.py +206 -0
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +146 -24
- flowfile_core/flowfile/flow_graph.py +504 -190
- flowfile_core/flowfile/flow_node/__init__.py +32 -0
- flowfile_core/flowfile/flow_node/executor.py +404 -0
- flowfile_core/flowfile/flow_node/flow_node.py +207 -106
- flowfile_core/flowfile/flow_node/models.py +40 -0
- flowfile_core/flowfile/flow_node/output_field_config_applier.py +217 -0
- flowfile_core/flowfile/flow_node/schema_utils.py +78 -0
- flowfile_core/flowfile/flow_node/state.py +155 -0
- flowfile_core/flowfile/history_manager.py +401 -0
- flowfile_core/flowfile/manage/compatibility_enhancements.py +9 -0
- flowfile_core/flowfile/manage/io_flowfile.py +3 -1
- flowfile_core/flowfile/sources/external_sources/sql_source/models.py +20 -4
- flowfile_core/flowfile/util/execution_orderer.py +89 -36
- flowfile_core/routes/auth.py +8 -9
- flowfile_core/routes/routes.py +320 -101
- flowfile_core/routes/user_defined_components.py +18 -16
- flowfile_core/schemas/history_schema.py +220 -0
- flowfile_core/schemas/input_schema.py +130 -6
- flowfile_core/schemas/schemas.py +9 -0
- flowfile_core/schemas/transform_schema.py +27 -5
- flowfile_core/schemas/yaml_types.py +23 -5
- flowfile_frame/adding_expr.py +18 -126
- flowfile_frame/callable_utils.py +261 -0
- flowfile_frame/database/connection_manager.py +0 -1
- flowfile_frame/expr.py +8 -4
- flowfile_frame/flow_frame.py +41 -41
- flowfile_frame/lazy.py +3 -12
- flowfile_frame/lazy_methods.py +5 -64
- flowfile_frame/utils.py +13 -32
- flowfile_worker/funcs.py +6 -4
- flowfile_worker/main.py +2 -0
- flowfile_worker/models.py +31 -11
- flowfile_worker/routes.py +60 -35
- flowfile_worker/spawner.py +7 -1
- flowfile_worker/streaming.py +335 -0
- flowfile/web/static/assets/ContextMenu-366bf1b4.js +0 -9
- flowfile/web/static/assets/ContextMenu-85cf5b44.js +0 -9
- flowfile/web/static/assets/ContextMenu-9d28ae6d.js +0 -9
- flowfile/web/static/assets/Join-28b5e18f.css +0 -109
- flowfile/web/static/assets/Read-39b63932.js +0 -222
- flowfile/web/static/assets/VueGraphicWalker-430f0b86.css +0 -6
- flowfile/web/static/assets/database_reader-ce1e55f3.svg +0 -24
- flowfile/web/static/assets/database_writer-b4ad0753.svg +0 -23
- flowfile/web/static/assets/element-icons-9c88a535.woff +0 -0
- flowfile/web/static/assets/element-icons-de5eb258.ttf +0 -0
- flowfile/web/static/assets/genericNodeSettings-0155288b.js +0 -136
- flowfile/web/static/assets/genericNodeSettings-3b2507ea.css +0 -46
- flowfile/web/static/assets/index-aeec439d.js +0 -38
- flowfile/web/static/assets/index-ca6799de.js +0 -62760
- flowfile/web/static/assets/index-d60c9dd4.css +0 -10777
- flowfile/web/static/assets/nodeInput-d478b9ac.js +0 -2
- flowfile/web/static/assets/outputCsv-cc84e09f.css +0 -2499
- flowfile-0.5.6.dist-info/RECORD +0 -407
- /flowfile/web/static/assets/{AdminView-f53bad23.css → AdminView-B2Dthl3u.css} +0 -0
- /flowfile/web/static/assets/{CloudConnectionView-cf85f943.css → CloudConnectionView-BdFYGWV7.css} +0 -0
- /flowfile/web/static/assets/{ColumnActionInput-c44b7aee.css → ColumnActionInput-dCasSIC9.css} +0 -0
- /flowfile/web/static/assets/{ColumnSelector-371637fb.css → ColumnSelector-j6sEOjo1.css} +0 -0
- /flowfile/web/static/assets/{CustomNode-edb9b939.css → CustomNode-VPlajG0j.css} +0 -0
- /flowfile/web/static/assets/{DatabaseConnectionSettings-c20a1e16.css → DatabaseConnectionSettings-B78hXYgu.css} +0 -0
- /flowfile/web/static/assets/{DatabaseView-6655afd6.css → DatabaseView-B-_adk1s.css} +0 -0
- /flowfile/web/static/assets/{DocumentationView-9ea6e871.css → DocumentationView-CL7iipFL.css} +0 -0
- /flowfile/web/static/assets/{ExploreData-10c5acc8.css → ExploreData-DHjv0Plr.css} +0 -0
- /flowfile/web/static/assets/{LoginView-d325d632.css → LoginView-DN1BXY3e.css} +0 -0
- /flowfile/web/static/assets/{PivotValidation-0e905b1a.css → PivotValidation-DK-FARWe.css} +0 -0
- /flowfile/web/static/assets/{PivotValidation-41b57ad6.css → PivotValidation-FUa9F47u.css} +0 -0
- /flowfile/web/static/assets/{PolarsCode-2b1f1f23.css → PolarsCode-G-gRSrSc.css} +0 -0
- /flowfile/web/static/assets/{SQLQueryComponent-edb90b98.css → SQLQueryComponent-oAbWw0r-.css} +0 -0
- /flowfile/web/static/assets/{SecretSelector-6329f743.css → SecretSelector-CJSadIZx.css} +0 -0
- /flowfile/web/static/assets/{SecretsView-aa291340.css → SecretsView-DbzIRAba.css} +0 -0
- /flowfile/web/static/assets/{SettingsSection-8f980839.css → SettingsSection-BGcJnH6q.css} +0 -0
- /flowfile/web/static/assets/{SettingsSection-07fbbc39.css → SettingsSection-DDWn_EGW.css} +0 -0
- /flowfile/web/static/assets/{SetupView-ec26f76a.css → SetupView-CI1nd-5Z.css} +0 -0
- /flowfile/web/static/assets/{SliderInput-f2e4f23c.css → SliderInput-BRk-q_Dk.css} +0 -0
- /flowfile/web/static/assets/{UnavailableFields-394a1f78.css → UnavailableFields-DRKDImKe.css} +0 -0
- /flowfile/web/static/assets/{Unique-2b705521.css → Unique-Absb0aON.css} +0 -0
- /flowfile/web/static/assets/{UnpivotValidation-d5ca3b7b.css → UnpivotValidation-DSBkFgS-.css} +0 -0
- /flowfile/web/static/assets/{airbyte-292aa232.png → airbyte-W0xvIXwZ.png} +0 -0
- /flowfile/web/static/assets/{cloud_storage_reader-aa1415d6.png → cloud_storage_reader-3GpSCk90.png} +0 -0
- /flowfile/web/static/assets/{cross_join-d30c0290.png → cross_join-B0qpgYoV.png} +0 -0
- /flowfile/web/static/assets/{dropDown-1d6acbd9.css → dropDown-CE0VF5_P.css} +0 -0
- /flowfile/web/static/assets/{explore_data-8a0a2861.png → explore_data-tX6olPPL.png} +0 -0
- /flowfile/web/static/assets/{fa-brands-400-808443ae.ttf → fa-brands-400-D1LuMI3I.ttf} +0 -0
- /flowfile/web/static/assets/{fa-brands-400-d7236a19.woff2 → fa-brands-400-D_cYUPeE.woff2} +0 -0
- /flowfile/web/static/assets/{fa-regular-400-e3456d12.woff2 → fa-regular-400-BjRzuEpd.woff2} +0 -0
- /flowfile/web/static/assets/{fa-regular-400-54cf6086.ttf → fa-regular-400-DZaxPHgR.ttf} +0 -0
- /flowfile/web/static/assets/{fa-solid-900-aa759986.woff2 → fa-solid-900-CTAAxXor.woff2} +0 -0
- /flowfile/web/static/assets/{fa-solid-900-d2f05935.ttf → fa-solid-900-D0aA9rwL.ttf} +0 -0
- /flowfile/web/static/assets/{fa-v4compatibility-0ce9033c.woff2 → fa-v4compatibility-C9RhG_FT.woff2} +0 -0
- /flowfile/web/static/assets/{fa-v4compatibility-30f6abf6.ttf → fa-v4compatibility-CCth-dXg.ttf} +0 -0
- /flowfile/web/static/assets/{filter-d7708bda.png → filter-WRdZyUOw.png} +0 -0
- /flowfile/web/static/assets/{formula-eeeb1611.png → formula-CgM7uHVI.png} +0 -0
- /flowfile/web/static/assets/{fullEditor-fe9f7e18.css → fullEditor-CmDI7T9F.css} +0 -0
- /flowfile/web/static/assets/{fuzzy_match-40c161b2.png → fuzzy_match-Yon3k5Tc.png} +0 -0
- /flowfile/web/static/assets/{graph_solver-8b7888b8.png → graph_solver-BlMrBttD.png} +0 -0
- /flowfile/web/static/assets/{group_by-80561fc3.png → group_by-Gici0CSS.png} +0 -0
- /flowfile/web/static/assets/{input_data-ab2eb678.png → input_data-BRdGecLc.png} +0 -0
- /flowfile/web/static/assets/{join-349043ae.png → join-BITWRu73.png} +0 -0
- /flowfile/web/static/assets/{manual_input-ae98f31d.png → manual_input-CFvo_EUS.png} +0 -0
- /flowfile/web/static/assets/{old_join-5d0eb604.png → old_join-B9bkpPqv.png} +0 -0
- /flowfile/web/static/assets/{output-06ec0371.png → output-Dp7-ZpC4.png} +0 -0
- /flowfile/web/static/assets/{outputExcel-f5d272b2.css → outputExcel-CKgRe2iT.css} +0 -0
- /flowfile/web/static/assets/{outputParquet-54597c3c.css → outputParquet-d7j407cK.css} +0 -0
- /flowfile/web/static/assets/{pivot-9660df51.png → pivot-DSxKhNlD.png} +0 -0
- /flowfile/web/static/assets/{polars_code-05ce5dc6.png → polars_code-DxiztZ1c.png} +0 -0
- /flowfile/web/static/assets/{readCsv-3bfac4c3.css → readCsv-BG-1Jilp.css} +0 -0
- /flowfile/web/static/assets/{readExcel-3db6b763.css → readExcel-DBQXKPtC.css} +0 -0
- /flowfile/web/static/assets/{record_count-dab44eb5.png → record_count-DCeaLtpS.png} +0 -0
- /flowfile/web/static/assets/{record_id-0b15856b.png → record_id-FeUjyIFh.png} +0 -0
- /flowfile/web/static/assets/{sample-693a88b5.png → sample-DeqfRiB-.png} +0 -0
- /flowfile/web/static/assets/{select-b0d0437a.png → select-D4JjbdjS.png} +0 -0
- /flowfile/web/static/assets/{selectDynamic-f2fb394f.css → selectDynamic-CjeTPUUo.css} +0 -0
- /flowfile/web/static/assets/{sort-2aa579f0.png → sort-DGwUG9WS.png} +0 -0
- /flowfile/web/static/assets/{summarize-2a099231.png → summarize-DFaNHpfp.png} +0 -0
- /flowfile/web/static/assets/{text_to_rows-859b29ea.png → text_to_rows-BdiAewrN.png} +0 -0
- /flowfile/web/static/assets/{union-2d8609f4.png → union-DCK-LSMq.png} +0 -0
- /flowfile/web/static/assets/{unique-1958b98a.png → unique-CdP3zZIq.png} +0 -0
- /flowfile/web/static/assets/{unpivot-d3cb4b5b.png → unpivot-CHttrEt8.png} +0 -0
- /flowfile/web/static/assets/{user-defined-icon-0ae16c90.png → user-defined-icon-BcIp2Vzo.png} +0 -0
- /flowfile/web/static/assets/{view-7a0f0be1.png → view-DUSRwjvq.png} +0 -0
- {flowfile-0.5.6.dist-info → flowfile-0.6.1.dist-info}/entry_points.txt +0 -0
- {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
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
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/",
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
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/",
|
|
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
|
-
|
|
74
|
-
|
|
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(
|
|
185
|
-
|
|
186
|
-
return pl.LazyFrame.deserialize(io.BytesIO(
|
|
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
|
)
|