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
|
@@ -17,6 +17,7 @@ from flowfile_core.flowfile.flow_data_engine.subprocess_operations import (
|
|
|
17
17
|
get_external_df_result,
|
|
18
18
|
results_exists,
|
|
19
19
|
)
|
|
20
|
+
from flowfile_core.flowfile.flow_node.executor import NodeExecutor
|
|
20
21
|
from flowfile_core.flowfile.flow_node.models import (
|
|
21
22
|
NodeResults,
|
|
22
23
|
NodeSchemaInformation,
|
|
@@ -24,7 +25,10 @@ from flowfile_core.flowfile.flow_node.models import (
|
|
|
24
25
|
NodeStepSettings,
|
|
25
26
|
NodeStepStats,
|
|
26
27
|
)
|
|
28
|
+
from flowfile_core.flowfile.flow_node.output_field_config_applier import apply_output_field_config
|
|
27
29
|
from flowfile_core.flowfile.flow_node.schema_callback import SingleExecutionFuture
|
|
30
|
+
from flowfile_core.flowfile.flow_node.schema_utils import create_schema_callback_with_output_config
|
|
31
|
+
from flowfile_core.flowfile.flow_node.state import NodeExecutionState
|
|
28
32
|
from flowfile_core.flowfile.setting_generator import setting_generator, setting_updator
|
|
29
33
|
from flowfile_core.flowfile.utils import get_hash
|
|
30
34
|
from flowfile_core.schemas import input_schema, schemas
|
|
@@ -63,6 +67,8 @@ class FlowNode:
|
|
|
63
67
|
_cache_progress: (
|
|
64
68
|
ExternalDfFetcher | ExternalDatabaseFetcher | ExternalDatabaseWriter | ExternalCloudWriter | None
|
|
65
69
|
) = None
|
|
70
|
+
_execution_state: NodeExecutionState = None
|
|
71
|
+
_executor: NodeExecutor | None = None # Lazy-initialized
|
|
66
72
|
|
|
67
73
|
def __init__(
|
|
68
74
|
self,
|
|
@@ -130,6 +136,9 @@ class FlowNode:
|
|
|
130
136
|
self._schema_callback = None
|
|
131
137
|
self._state_needs_reset = False
|
|
132
138
|
self._execution_lock = threading.RLock() # Protects concurrent access to get_resulting_data
|
|
139
|
+
# Initialize execution state
|
|
140
|
+
self._execution_state = NodeExecutionState()
|
|
141
|
+
self._executor = None # Will be lazily created
|
|
133
142
|
|
|
134
143
|
@property
|
|
135
144
|
def state_needs_reset(self) -> bool:
|
|
@@ -192,12 +201,20 @@ class FlowNode:
|
|
|
192
201
|
def schema_callback(self, f: Callable):
|
|
193
202
|
"""Sets the schema callback function for the node.
|
|
194
203
|
|
|
204
|
+
If the node has an enabled output_field_config, the callback is automatically
|
|
205
|
+
wrapped to use the output_field_config schema for prediction.
|
|
206
|
+
|
|
195
207
|
Args:
|
|
196
208
|
f: The function to be used for schema calculation.
|
|
197
209
|
"""
|
|
198
210
|
if f is None:
|
|
199
211
|
return
|
|
200
212
|
|
|
213
|
+
# Wrap callback with output_field_config support if present and enabled
|
|
214
|
+
output_field_config = getattr(self._setting_input, 'output_field_config', None)
|
|
215
|
+
if output_field_config and output_field_config.enabled:
|
|
216
|
+
f = create_schema_callback_with_output_config(f, output_field_config)
|
|
217
|
+
|
|
201
218
|
def error_callback(e: Exception) -> list:
|
|
202
219
|
logger.warning(e)
|
|
203
220
|
|
|
@@ -206,6 +223,17 @@ class FlowNode:
|
|
|
206
223
|
|
|
207
224
|
self._schema_callback = SingleExecutionFuture(f, error_callback)
|
|
208
225
|
|
|
226
|
+
@property
|
|
227
|
+
def executor(self) -> NodeExecutor:
|
|
228
|
+
"""Lazy-initialized executor instance.
|
|
229
|
+
|
|
230
|
+
Reusing the same executor avoids object creation overhead
|
|
231
|
+
when execute_node is called multiple times.
|
|
232
|
+
"""
|
|
233
|
+
if self._executor is None:
|
|
234
|
+
self._executor = NodeExecutor(self)
|
|
235
|
+
return self._executor
|
|
236
|
+
|
|
209
237
|
@property
|
|
210
238
|
def is_start(self) -> bool:
|
|
211
239
|
"""Determines if the node is a starting node in the flow.
|
|
@@ -328,6 +356,9 @@ class FlowNode:
|
|
|
328
356
|
if is_manual_input:
|
|
329
357
|
_ = self.hash
|
|
330
358
|
self._setting_input = setting_input
|
|
359
|
+
# Copy cache_results from setting_input to node_settings
|
|
360
|
+
if hasattr(setting_input, "cache_results"):
|
|
361
|
+
self.node_settings.cache_results = setting_input.cache_results
|
|
331
362
|
self.set_node_information()
|
|
332
363
|
if is_manual_input:
|
|
333
364
|
if self.hash != self.calculate_hash(setting_input) or not self.node_stats.has_run_with_current_setup:
|
|
@@ -393,7 +424,6 @@ class FlowNode:
|
|
|
393
424
|
|
|
394
425
|
This includes the node's connections, settings, and position.
|
|
395
426
|
"""
|
|
396
|
-
logger.info("setting node information")
|
|
397
427
|
node_information = self.node_information
|
|
398
428
|
node_information.left_input_id = self.node_inputs.left_input.node_id if self.left_input else None
|
|
399
429
|
node_information.right_input_id = self.node_inputs.right_input.node_id if self.right_input else None
|
|
@@ -405,6 +435,9 @@ class FlowNode:
|
|
|
405
435
|
node_information.description = (
|
|
406
436
|
self.setting_input.description if hasattr(self.setting_input, "description") else ""
|
|
407
437
|
)
|
|
438
|
+
node_information.node_reference = (
|
|
439
|
+
self.setting_input.node_reference if hasattr(self.setting_input, "node_reference") else None
|
|
440
|
+
)
|
|
408
441
|
node_information.is_setup = self.is_setup
|
|
409
442
|
node_information.x_position = self.setting_input.pos_x
|
|
410
443
|
node_information.y_position = self.setting_input.pos_y
|
|
@@ -534,23 +567,60 @@ class FlowNode:
|
|
|
534
567
|
Returns:
|
|
535
568
|
A list of FlowfileColumn objects representing the predicted schema.
|
|
536
569
|
"""
|
|
570
|
+
logger.info(
|
|
571
|
+
f"get_predicted_schema: node_id={self.node_id}, node_type={self.node_type}, force={force}, "
|
|
572
|
+
f"has_predicted_schema={self.node_schema.predicted_schema is not None}, "
|
|
573
|
+
f"has_schema_callback={self.schema_callback is not None}, "
|
|
574
|
+
f"has_output_field_config={hasattr(self._setting_input, 'output_field_config') and self._setting_input.output_field_config is not None if self._setting_input else False}"
|
|
575
|
+
)
|
|
537
576
|
|
|
538
577
|
if self.node_schema.predicted_schema and not force:
|
|
578
|
+
logger.debug(f"get_predicted_schema: node_id={self.node_id} - returning cached predicted_schema")
|
|
539
579
|
return self.node_schema.predicted_schema
|
|
580
|
+
|
|
540
581
|
if self.schema_callback is not None and (self.node_schema.predicted_schema is None or force):
|
|
541
582
|
self.print("Getting the data from a schema callback")
|
|
583
|
+
logger.info(f"get_predicted_schema: node_id={self.node_id} - invoking schema_callback")
|
|
542
584
|
if force:
|
|
543
585
|
# Force the schema callback to reset, so that it will be executed again
|
|
586
|
+
logger.debug(f"get_predicted_schema: node_id={self.node_id} - forcing schema_callback reset")
|
|
544
587
|
self.schema_callback.reset()
|
|
545
|
-
|
|
588
|
+
|
|
589
|
+
try:
|
|
590
|
+
schema = self.schema_callback()
|
|
591
|
+
logger.info(
|
|
592
|
+
f"get_predicted_schema: node_id={self.node_id} - schema_callback returned "
|
|
593
|
+
f"{len(schema) if schema else 0} columns: {[c.name for c in schema] if schema else []}"
|
|
594
|
+
)
|
|
595
|
+
except Exception as e:
|
|
596
|
+
logger.error(f"get_predicted_schema: node_id={self.node_id} - schema_callback raised exception: {e}")
|
|
597
|
+
schema = None
|
|
598
|
+
|
|
546
599
|
if schema is not None and len(schema) > 0:
|
|
547
600
|
self.print("Calculating the schema based on the schema callback")
|
|
548
601
|
self.node_schema.predicted_schema = schema
|
|
602
|
+
logger.info(f"get_predicted_schema: node_id={self.node_id} - set predicted_schema from schema_callback")
|
|
549
603
|
return self.node_schema.predicted_schema
|
|
604
|
+
else:
|
|
605
|
+
logger.warning(f"get_predicted_schema: node_id={self.node_id} - schema_callback returned empty/None schema")
|
|
606
|
+
else:
|
|
607
|
+
logger.debug(f"get_predicted_schema: node_id={self.node_id} - no schema_callback available")
|
|
608
|
+
|
|
609
|
+
logger.debug(f"get_predicted_schema: node_id={self.node_id} - falling back to _predicted_data_getter")
|
|
550
610
|
predicted_data = self._predicted_data_getter()
|
|
551
611
|
if predicted_data is not None and predicted_data.schema is not None:
|
|
552
612
|
self.print("Calculating the schema based on the predicted resulting data")
|
|
613
|
+
logger.info(
|
|
614
|
+
f"get_predicted_schema: node_id={self.node_id} - using schema from predicted_data "
|
|
615
|
+
f"({len(predicted_data.schema)} columns)"
|
|
616
|
+
)
|
|
553
617
|
self.node_schema.predicted_schema = self._predicted_data_getter().schema
|
|
618
|
+
else:
|
|
619
|
+
logger.warning(
|
|
620
|
+
f"get_predicted_schema: node_id={self.node_id} - no schema available from any source "
|
|
621
|
+
f"(predicted_data={'None' if predicted_data is None else 'has_data'}, "
|
|
622
|
+
f"schema={'None' if predicted_data is None or predicted_data.schema is None else 'has_schema'})"
|
|
623
|
+
)
|
|
554
624
|
|
|
555
625
|
return self.node_schema.predicted_schema
|
|
556
626
|
|
|
@@ -579,7 +649,8 @@ class FlowNode:
|
|
|
579
649
|
"""Executes the node's function to produce the actual output data.
|
|
580
650
|
|
|
581
651
|
Handles both regular functions and external data sources.
|
|
582
|
-
Thread-safe: uses _execution_lock to prevent concurrent execution
|
|
652
|
+
Thread-safe: uses _execution_lock to prevent concurrent execution
|
|
653
|
+
and concurrent access to the underlying LazyFrame by sibling nodes.
|
|
583
654
|
|
|
584
655
|
Returns:
|
|
585
656
|
A FlowDataEngine instance containing the result, or None on error.
|
|
@@ -602,17 +673,34 @@ class FlowNode:
|
|
|
602
673
|
try:
|
|
603
674
|
self.print("Collecting input data from all inputs")
|
|
604
675
|
input_data = []
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
676
|
+
input_locks = []
|
|
677
|
+
try:
|
|
678
|
+
for i, v in enumerate(self.all_inputs):
|
|
679
|
+
self.print(f"Getting resulting data from input {i} (node {v.node_id})")
|
|
680
|
+
# Lock the input node to prevent sibling nodes from
|
|
681
|
+
# concurrently accessing the same upstream LazyFrame.
|
|
682
|
+
v._execution_lock.acquire()
|
|
683
|
+
input_locks.append(v._execution_lock)
|
|
684
|
+
input_result = v.get_resulting_data()
|
|
685
|
+
self.print(f"Input {i} data type: {type(input_result)}, dataframe type: {type(input_result.data_frame) if input_result else 'None'}")
|
|
686
|
+
input_data.append(input_result)
|
|
687
|
+
self.print(f"All {len(input_data)} inputs collected, calling node function")
|
|
688
|
+
fl = self._function(*input_data)
|
|
689
|
+
finally:
|
|
690
|
+
for lock in input_locks:
|
|
691
|
+
lock.release()
|
|
613
692
|
except Exception as e:
|
|
614
693
|
raise e
|
|
615
694
|
fl.set_streamable(self.node_settings.streamable)
|
|
695
|
+
|
|
696
|
+
# Apply output field configuration if enabled
|
|
697
|
+
if hasattr(self._setting_input, 'output_field_config') and self._setting_input.output_field_config:
|
|
698
|
+
try:
|
|
699
|
+
fl = apply_output_field_config(fl, self._setting_input.output_field_config)
|
|
700
|
+
except Exception as e:
|
|
701
|
+
logger.error(f"Error applying output field config for node {self.node_id}: {e}")
|
|
702
|
+
raise
|
|
703
|
+
|
|
616
704
|
self.results.resulting_data = fl
|
|
617
705
|
self.node_schema.result_schema = fl.schema
|
|
618
706
|
except Exception as e:
|
|
@@ -633,6 +721,13 @@ class FlowNode:
|
|
|
633
721
|
"""
|
|
634
722
|
try:
|
|
635
723
|
fl = self._function(*[v.get_predicted_resulting_data() for v in self.all_inputs])
|
|
724
|
+
|
|
725
|
+
# Apply output field configuration if enabled (mirrors get_resulting_data behavior)
|
|
726
|
+
# This ensures schema prediction accounts for output_field_config validation
|
|
727
|
+
if hasattr(self._setting_input, 'output_field_config') and self._setting_input.output_field_config:
|
|
728
|
+
if self._setting_input.output_field_config.enabled:
|
|
729
|
+
fl = apply_output_field_config(fl, self._setting_input.output_field_config)
|
|
730
|
+
|
|
636
731
|
return fl
|
|
637
732
|
except ValueError as e:
|
|
638
733
|
if str(e) == "generator already executing":
|
|
@@ -768,9 +863,50 @@ class FlowNode:
|
|
|
768
863
|
"""Makes the node instance callable, acting as an alias for execute_node."""
|
|
769
864
|
self.execute_node(*args, **kwargs)
|
|
770
865
|
|
|
771
|
-
def
|
|
866
|
+
def _can_skip_execution_fast(
|
|
867
|
+
self,
|
|
868
|
+
run_location: schemas.ExecutionLocationsLiteral,
|
|
869
|
+
performance_mode: bool,
|
|
870
|
+
reset_cache: bool,
|
|
871
|
+
) -> bool:
|
|
872
|
+
"""Fast-path check to avoid executor overhead when we can skip.
|
|
873
|
+
|
|
874
|
+
This inlines the most common skip conditions to avoid
|
|
875
|
+
creating an executor instance when not needed.
|
|
876
|
+
|
|
877
|
+
Returns True if execution can definitely be skipped.
|
|
878
|
+
Returns False if full execution logic is needed.
|
|
879
|
+
"""
|
|
880
|
+
# Can't skip if forced refresh
|
|
881
|
+
if reset_cache:
|
|
882
|
+
return False
|
|
883
|
+
|
|
884
|
+
# Output nodes always run
|
|
885
|
+
if self.node_template.node_group == "output":
|
|
886
|
+
return False
|
|
887
|
+
|
|
888
|
+
# Must run if never ran before
|
|
889
|
+
if not self._execution_state.has_run_with_current_setup:
|
|
890
|
+
return False
|
|
891
|
+
|
|
892
|
+
# Check for source file changes (read nodes only)
|
|
893
|
+
if self.node_type == "read" and self._execution_state.source_file_info:
|
|
894
|
+
if self._execution_state.source_file_info.has_changed():
|
|
895
|
+
return False
|
|
896
|
+
|
|
897
|
+
# Cache-enabled nodes: only skip if the cache file is still present
|
|
898
|
+
if self.node_settings.cache_results:
|
|
899
|
+
return results_exists(self.hash)
|
|
900
|
+
|
|
901
|
+
# Already ran with current settings → skip
|
|
902
|
+
# Results are available in memory from previous execution
|
|
903
|
+
return True
|
|
904
|
+
|
|
905
|
+
def _do_execute_full_local(self, performance_mode: bool = False) -> None:
|
|
772
906
|
"""Executes the node's logic locally, including example data generation.
|
|
773
907
|
|
|
908
|
+
Internal method called by NodeExecutor.
|
|
909
|
+
|
|
774
910
|
Args:
|
|
775
911
|
performance_mode: If True, skips generating example data.
|
|
776
912
|
|
|
@@ -798,12 +934,14 @@ class FlowNode:
|
|
|
798
934
|
self.node_schema.result_schema = self.results.resulting_data.schema
|
|
799
935
|
self.node_stats.has_completed_last_run = True
|
|
800
936
|
|
|
801
|
-
def
|
|
802
|
-
"""Executes the node's logic locally.
|
|
937
|
+
def _do_execute_local_with_sampling(self, performance_mode: bool = False, flow_id: int = None):
|
|
938
|
+
"""Executes the node's logic locally with external sampling.
|
|
939
|
+
|
|
940
|
+
Internal method called by NodeExecutor.
|
|
803
941
|
|
|
804
942
|
Args:
|
|
805
|
-
flow_id: The ID of the parent flow.
|
|
806
943
|
performance_mode: If True, skips generating example data.
|
|
944
|
+
flow_id: The ID of the parent flow.
|
|
807
945
|
|
|
808
946
|
Raises:
|
|
809
947
|
Exception: Propagates exceptions from the execution.
|
|
@@ -836,9 +974,11 @@ class FlowNode:
|
|
|
836
974
|
if not self.node_settings.streamable:
|
|
837
975
|
step.node_settings.streamable = self.node_settings.streamable
|
|
838
976
|
|
|
839
|
-
def
|
|
977
|
+
def _do_execute_remote(self, performance_mode: bool = False, node_logger: NodeLogger = None):
|
|
840
978
|
"""Executes the node's logic remotely or handles cached results.
|
|
841
979
|
|
|
980
|
+
Internal method called by NodeExecutor.
|
|
981
|
+
|
|
842
982
|
Args:
|
|
843
983
|
performance_mode: If True, skips generating example data.
|
|
844
984
|
node_logger: The logger for this node execution.
|
|
@@ -920,6 +1060,19 @@ class FlowNode:
|
|
|
920
1060
|
finally:
|
|
921
1061
|
self._fetch_cached_df = None
|
|
922
1062
|
|
|
1063
|
+
# Backward-compatible aliases for renamed methods
|
|
1064
|
+
def execute_full_local(self, performance_mode: bool = False) -> None:
|
|
1065
|
+
"""Backward-compatible alias for _do_execute_full_local."""
|
|
1066
|
+
return self._do_execute_full_local(performance_mode)
|
|
1067
|
+
|
|
1068
|
+
def execute_local(self, flow_id: int, performance_mode: bool = False):
|
|
1069
|
+
"""Backward-compatible alias for _do_execute_local_with_sampling."""
|
|
1070
|
+
return self._do_execute_local_with_sampling(performance_mode, flow_id)
|
|
1071
|
+
|
|
1072
|
+
def execute_remote(self, performance_mode: bool = False, node_logger: NodeLogger = None):
|
|
1073
|
+
"""Backward-compatible alias for _do_execute_remote."""
|
|
1074
|
+
return self._do_execute_remote(performance_mode, node_logger)
|
|
1075
|
+
|
|
923
1076
|
def prepare_before_run(self):
|
|
924
1077
|
"""Resets results and errors before a new execution."""
|
|
925
1078
|
|
|
@@ -945,103 +1098,42 @@ class FlowNode:
|
|
|
945
1098
|
retry: bool = True,
|
|
946
1099
|
node_logger: NodeLogger = None,
|
|
947
1100
|
optimize_for_downstream: bool = True,
|
|
948
|
-
):
|
|
949
|
-
"""
|
|
1101
|
+
) -> None:
|
|
1102
|
+
"""Execute the node based on its current state and settings.
|
|
950
1103
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
performance_mode: If True, optimizes for speed over diagnostics.
|
|
955
|
-
retry: If True, allows retrying execution on recoverable errors.
|
|
956
|
-
node_logger: The logger for this node execution.
|
|
957
|
-
optimize_for_downstream: If true, operations that shuffle the order of rows are fully cached and provided as
|
|
958
|
-
input to downstream steps
|
|
1104
|
+
This method uses a fast-path to quickly skip execution when possible,
|
|
1105
|
+
avoiding executor overhead. For cases requiring full execution logic,
|
|
1106
|
+
it delegates to the NodeExecutor.
|
|
959
1107
|
|
|
960
|
-
|
|
961
|
-
|
|
1108
|
+
Args:
|
|
1109
|
+
run_location: Where to execute ('local' or 'remote')
|
|
1110
|
+
reset_cache: Force cache invalidation
|
|
1111
|
+
performance_mode: Skip example data generation for speed
|
|
1112
|
+
retry: Allow retry on recoverable errors
|
|
1113
|
+
node_logger: Logger for this node's execution
|
|
1114
|
+
optimize_for_downstream: Cache wide transforms for downstream nodes
|
|
962
1115
|
"""
|
|
963
1116
|
if node_logger is None:
|
|
964
|
-
raise
|
|
965
|
-
# TODO: Simplify which route is being picked there are many duplicate checks
|
|
1117
|
+
raise ValueError("node_logger is required")
|
|
966
1118
|
|
|
967
|
-
if
|
|
968
|
-
self.
|
|
969
|
-
|
|
970
|
-
self.node_stats.has_completed_last_run = False
|
|
1119
|
+
if not self.is_setup:
|
|
1120
|
+
node_logger.warning(f"Node {self.__name__} is not setup, cannot run")
|
|
1121
|
+
return
|
|
971
1122
|
|
|
972
|
-
if
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
or self.node_template.node_group == "output"
|
|
977
|
-
and not (run_location == "local")
|
|
978
|
-
):
|
|
979
|
-
self.clear_table_example()
|
|
980
|
-
self.prepare_before_run()
|
|
981
|
-
self.reset()
|
|
982
|
-
try:
|
|
983
|
-
if (
|
|
984
|
-
run_location == "remote"
|
|
985
|
-
or (self.node_default.transform_type == "wide" and optimize_for_downstream)
|
|
986
|
-
and not run_location == "local"
|
|
987
|
-
) or self.node_settings.cache_results:
|
|
988
|
-
node_logger.info("Running the node remotely")
|
|
989
|
-
if self.node_settings.cache_results:
|
|
990
|
-
performance_mode = False
|
|
991
|
-
self.execute_remote(
|
|
992
|
-
performance_mode=(performance_mode if not self.node_settings.cache_results else False),
|
|
993
|
-
node_logger=node_logger,
|
|
994
|
-
)
|
|
995
|
-
else:
|
|
996
|
-
node_logger.info("Running the node locally")
|
|
997
|
-
self.execute_local(performance_mode=performance_mode, flow_id=node_logger.flow_id)
|
|
998
|
-
except Exception as e:
|
|
999
|
-
if "No such file or directory (os error" in str(e) and retry:
|
|
1000
|
-
logger.warning("Error with the input node, starting to rerun the input node...")
|
|
1001
|
-
all_inputs: list[FlowNode] = self.node_inputs.get_all_inputs()
|
|
1002
|
-
for node_input in all_inputs:
|
|
1003
|
-
node_input.execute_node(
|
|
1004
|
-
run_location=run_location,
|
|
1005
|
-
performance_mode=performance_mode,
|
|
1006
|
-
retry=True,
|
|
1007
|
-
reset_cache=True,
|
|
1008
|
-
node_logger=node_logger,
|
|
1009
|
-
)
|
|
1010
|
-
self.execute_node(
|
|
1011
|
-
run_location=run_location,
|
|
1012
|
-
performance_mode=performance_mode,
|
|
1013
|
-
retry=False,
|
|
1014
|
-
node_logger=node_logger,
|
|
1015
|
-
)
|
|
1016
|
-
else:
|
|
1017
|
-
self.results.errors = str(e)
|
|
1018
|
-
if "Connection refused" in str(e) and "/submit_query/" in str(e):
|
|
1019
|
-
node_logger.warning(
|
|
1020
|
-
"There was an issue connecting to the remote worker, "
|
|
1021
|
-
"ensure the worker process is running, "
|
|
1022
|
-
"or change the settings to, so it executes locally"
|
|
1023
|
-
)
|
|
1024
|
-
node_logger.error(
|
|
1025
|
-
"Could not execute in the remote worker. (Re)start the worker service, or change settings to local settings."
|
|
1026
|
-
)
|
|
1027
|
-
else:
|
|
1028
|
-
node_logger.error(f"Error with running the node: {e}")
|
|
1029
|
-
elif (run_location == "local") and (
|
|
1030
|
-
not self.node_stats.has_run_with_current_setup or self.node_template.node_group == "output"
|
|
1031
|
-
):
|
|
1032
|
-
try:
|
|
1033
|
-
node_logger.info("Executing fully locally")
|
|
1034
|
-
self.execute_full_local(performance_mode)
|
|
1035
|
-
except Exception as e:
|
|
1036
|
-
self.results.errors = str(e)
|
|
1037
|
-
node_logger.error(f"Error with running the node: {e}")
|
|
1038
|
-
self.node_stats.error = str(e)
|
|
1039
|
-
self.node_stats.has_completed_last_run = False
|
|
1123
|
+
# Fast-path: check if we can skip without creating executor
|
|
1124
|
+
if self._can_skip_execution_fast(run_location, performance_mode, reset_cache):
|
|
1125
|
+
node_logger.info("Node is up-to-date, skipping execution")
|
|
1126
|
+
return
|
|
1040
1127
|
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1128
|
+
# Full execution logic via executor
|
|
1129
|
+
self.executor.execute(
|
|
1130
|
+
run_location=run_location,
|
|
1131
|
+
reset_cache=reset_cache,
|
|
1132
|
+
performance_mode=performance_mode,
|
|
1133
|
+
retry=retry,
|
|
1134
|
+
node_logger=node_logger,
|
|
1135
|
+
optimize_for_downstream=optimize_for_downstream,
|
|
1136
|
+
)
|
|
1045
1137
|
|
|
1046
1138
|
def store_example_data_generator(self, external_df_fetcher: ExternalDfFetcher | ExternalSampler):
|
|
1047
1139
|
"""Stores a generator function for fetching a sample of the result data.
|
|
@@ -1082,6 +1174,15 @@ class FlowNode:
|
|
|
1082
1174
|
self._hash = None
|
|
1083
1175
|
self.node_information.is_setup = None
|
|
1084
1176
|
self.results.errors = None
|
|
1177
|
+
|
|
1178
|
+
# Reset execution state but preserve source file info for change detection
|
|
1179
|
+
self._execution_state.has_run_with_current_setup = False
|
|
1180
|
+
self._execution_state.has_completed_last_run = False
|
|
1181
|
+
self._execution_state.result_schema = None
|
|
1182
|
+
self._execution_state.predicted_schema = None
|
|
1183
|
+
self._execution_state.execution_hash = None
|
|
1184
|
+
# Note: source_file_info NOT reset - needed for change detection
|
|
1185
|
+
|
|
1085
1186
|
if self.is_correct:
|
|
1086
1187
|
self._schema_callback = None # Ensure the schema callback is reset
|
|
1087
1188
|
if self.schema_callback:
|
|
@@ -1,9 +1,49 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
+
from enum import Enum, auto
|
|
3
4
|
from typing import Literal
|
|
4
5
|
|
|
5
6
|
import pyarrow as pa
|
|
6
7
|
|
|
8
|
+
|
|
9
|
+
class ExecutionStrategy(Enum):
|
|
10
|
+
"""
|
|
11
|
+
Determines HOW the node will be executed.
|
|
12
|
+
|
|
13
|
+
Used by NodeExecutor to dispatch to the correct execution method.
|
|
14
|
+
"""
|
|
15
|
+
SKIP = auto() # Already up-to-date, don't execute
|
|
16
|
+
FULL_LOCAL = auto() # 100% in-process (WASM, simple cases)
|
|
17
|
+
LOCAL_WITH_SAMPLING = auto() # In-process + external sampler for preview
|
|
18
|
+
REMOTE = auto() # Full external worker execution
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InvalidationReason(Enum):
|
|
22
|
+
"""
|
|
23
|
+
Why does this node need to run?
|
|
24
|
+
|
|
25
|
+
Used for logging and debugging execution decisions.
|
|
26
|
+
"""
|
|
27
|
+
NEVER_RAN = auto() # First execution
|
|
28
|
+
SETTINGS_CHANGED = auto() # Node configuration changed
|
|
29
|
+
SOURCE_FILE_CHANGED = auto() # Input file modified (read nodes)
|
|
30
|
+
CACHE_MISSING = auto() # Cache enabled but no cached result
|
|
31
|
+
FORCED_REFRESH = auto() # User requested reset_cache=True
|
|
32
|
+
OUTPUT_NODE = auto() # Output nodes always execute
|
|
33
|
+
PERFORMANCE_MODE = auto() # Running in performance mode (no caching)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class ExecutionDecision:
|
|
38
|
+
"""
|
|
39
|
+
Result of deciding whether and how to execute a node.
|
|
40
|
+
|
|
41
|
+
Encapsulates the execution decision logic result.
|
|
42
|
+
"""
|
|
43
|
+
should_run: bool
|
|
44
|
+
strategy: ExecutionStrategy
|
|
45
|
+
reason: InvalidationReason | None = None
|
|
46
|
+
|
|
7
47
|
# Forward declaration for type hints to avoid circular imports
|
|
8
48
|
if False:
|
|
9
49
|
from flowfile_core.flowfile.flow_node.flow_node import FlowNode
|