Flowfile 0.5.4__py3-none-any.whl → 0.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flowfile/__main__.py +94 -1
- flowfile/api.py +8 -6
- flowfile/web/static/assets/{AdminView-f9847d67.js → AdminView-C4K1DdHI.js} +28 -33
- flowfile/web/static/assets/{CloudConnectionView-faace55b.js → CloudConnectionView-BZbPvPUL.js} +39 -50
- flowfile/web/static/assets/{CloudStorageReader-24c54524.css → CloudStorageReader-BDByiqPI.css} +25 -25
- flowfile/web/static/assets/{CloudStorageReader-d86ecaa7.js → CloudStorageReader-DLVukNJ7.js} +30 -35
- flowfile/web/static/assets/{CloudStorageWriter-0f4d9a44.js → CloudStorageWriter-Bfi-C1QW.js} +32 -37
- flowfile/web/static/assets/{CloudStorageWriter-60547855.css → CloudStorageWriter-y8jL8yjG.css} +24 -24
- flowfile/web/static/assets/{ColumnActionInput-f4189ae0.js → ColumnActionInput-BpiCApw9.js} +7 -12
- flowfile/web/static/assets/{ColumnSelector-e66b33da.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-a1bd6314.js → ContextMenu.vue_vue_type_script_setup_true_lang-I4rXXd6G.js} +4 -5
- flowfile/web/static/assets/{CrossJoin-24694b8f.js → CrossJoin-BOFfxkJO.js} +19 -18
- flowfile/web/static/assets/{CrossJoin-71b4cc10.css → CrossJoin-Cmbyt9im.css} +18 -18
- flowfile/web/static/assets/{CustomNode-569d45ff.js → CustomNode-Bhpezobq.js} +12 -17
- flowfile/web/static/assets/{DatabaseConnectionSettings-cfc08938.js → DatabaseConnectionSettings-Dw3bSJKB.js} +10 -11
- flowfile/web/static/assets/{DatabaseReader-5bf8c75b.css → DatabaseReader-D6pUNUCs.css} +21 -21
- flowfile/web/static/assets/{DatabaseReader-701feabb.js → DatabaseReader-m87ghlw0.js} +36 -34
- flowfile/web/static/assets/{DatabaseView-0482e5b5.js → DatabaseView-CisSAtpe.js} +30 -38
- flowfile/web/static/assets/{DatabaseWriter-16721989.js → DatabaseWriter-Bbj9JLdL.js} +33 -35
- flowfile/web/static/assets/{DatabaseWriter-bdcf2c8b.css → DatabaseWriter-RBqdFLj8.css} +17 -17
- flowfile/web/static/assets/{DesignerView-f64749fb.js → DesignerView-DemDevTQ.js} +1841 -2090
- flowfile/web/static/assets/{DesignerView-49abb835.css → DesignerView-Dm6OzlIc.css} +244 -202
- flowfile/web/static/assets/{DocumentationView-61bd2990.js → DocumentationView-BrC1ZR3H.js} +3 -4
- flowfile/web/static/assets/{ExploreData-e2735b13.js → ExploreData-BMKcDuRb.js} +8 -10
- flowfile/web/static/assets/{ExternalSource-2535c3b2.js → ExternalSource-BXrNNS-f.js} +40 -42
- flowfile/web/static/assets/{ExternalSource-7ac7373f.css → ExternalSource-NB6WVl5R.css} +14 -14
- flowfile/web/static/assets/{Filter-2cdbc93c.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-fcda3c2c.js → Formula-ufuy4mVD.js} +27 -26
- flowfile/web/static/assets/{FuzzyMatch-ad6361d6.css → FuzzyMatch-BGJAwgd0.css} +42 -42
- flowfile/web/static/assets/{FuzzyMatch-f8d3b7d3.js → FuzzyMatch-BOHODq3h.js} +36 -38
- flowfile/web/static/assets/{GraphSolver-72eaa695.js → GraphSolver-B6ZzpNGO.js} +23 -21
- flowfile/web/static/assets/{GraphSolver-4b4d7db9.css → GraphSolver-DFN83sj3.css} +4 -4
- flowfile/web/static/assets/{GroupBy-8aa0598b.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-e40f0ffa.js → Join-DsBEy1IH.js} +48 -43
- flowfile/web/static/assets/{LoginView-5111c9ae.js → LoginView-Ct0rhdcO.js} +1 -2
- flowfile/web/static/assets/{ManualInput-3702e677.css → ManualInput-DlZmtMdt.css} +48 -48
- flowfile/web/static/assets/{ManualInput-9b6f3224.js → ManualInput-bC4BUgnG.js} +85 -44
- flowfile/web/static/assets/{MultiSelect-ef28e19e.js → MultiSelect-DIQ8PuTC.js} +2 -2
- flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-83b3bbfd.js → MultiSelect.vue_vue_type_script_setup_true_lang-BefHfqTI.js} +1 -1
- flowfile/web/static/assets/{NodeDesigner-d2b7ee2b.js → NodeDesigner-D39yzr2k.js} +178 -208
- flowfile/web/static/assets/{NodeDesigner-94cd4dd3.css → NodeDesigner-R0l6sYyY.css} +76 -76
- flowfile/web/static/assets/{NumericInput-1d789794.js → NumericInput-DMSX3oOr.js} +2 -2
- flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-7775f83e.js → NumericInput.vue_vue_type_script_setup_true_lang-d0YlVHAl.js} +1 -1
- flowfile/web/static/assets/{Output-cefef801.js → Output-D0VoXGcW.js} +26 -34
- flowfile/web/static/assets/{Output-692dd25d.css → Output-DsmglIDy.css} +5 -5
- flowfile/web/static/assets/{Pivot-bab1b75b.js → Pivot-BnMB4sEe.js} +26 -26
- flowfile/web/static/assets/{Pivot-0eda81b4.css → Pivot-qKTyWxop.css} +4 -4
- flowfile/web/static/assets/{PivotValidation-fba09336.js → PivotValidation-B2lWvugt.js} +7 -9
- flowfile/web/static/assets/{PivotValidation-e7941f91.js → PivotValidation-BPlhRjpL.js} +7 -9
- flowfile/web/static/assets/{PolarsCode-740e40fa.js → PolarsCode-5h0tHnWR.js} +22 -20
- flowfile/web/static/assets/PopOver-BHpt5rsj.js +134 -0
- 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-ffc71eca.js → RecordCount-DkVixq9v.js} +18 -17
- flowfile/web/static/assets/{RecordId-a70bb8df.js → RecordId-C2UEGlCf.js} +42 -39
- flowfile/web/static/assets/{SQLQueryComponent-15a421f5.js → SQLQueryComponent-Dr5KMoD3.js} +2 -3
- flowfile/web/static/assets/{Sample-6c26afc7.js → Sample-Cb3eQNmd.js} +30 -30
- flowfile/web/static/assets/{SecretSelector-ceed9496.js → SecretSelector-De2L2bSx.js} +3 -4
- flowfile/web/static/assets/{SecretsView-214d255a.js → SecretsView-CheC9BPV.js} +13 -16
- flowfile/web/static/assets/{Select-8fc29999.js → Select-CI8TloRs.js} +41 -36
- flowfile/web/static/assets/{SettingsSection-9f0d1725.js → SettingsSection-B39ulIiI.js} +1 -2
- flowfile/web/static/assets/{SettingsSection-83090218.js → SettingsSection-BiCc7S9h.js} +1 -2
- flowfile/web/static/assets/{SettingsSection-3f70e4c3.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-3fa0aa03.js → SetupView-C1aXRDvp.js} +3 -4
- flowfile/web/static/assets/{SetupView-e2da3442.css → SetupView-CI1nd-5Z.css} +38 -38
- flowfile/web/static/assets/{SingleSelect-a4a568cb.js → SingleSelect-Kr_hz90m.js} +2 -2
- flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-c8ebdd33.js → SingleSelect.vue_vue_type_script_setup_true_lang-Rxht5Z5N.js} +1 -1
- flowfile/web/static/assets/{SliderInput-be533e71.js → SliderInput-CLqpCxCb.js} +1 -2
- flowfile/web/static/assets/{GroupBy-5792782d.css → Sort-BIt2kc_p.css} +1 -1
- flowfile/web/static/assets/{Sort-154dad81.js → Sort-Dnw_J6Qi.js} +25 -25
- flowfile/web/static/assets/{TextInput-454e2bda.js → TextInput-wdlunIZC.js} +2 -2
- flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-e86510d0.js → TextInput.vue_vue_type_script_setup_true_lang-Bcj3ywzv.js} +1 -1
- flowfile/web/static/assets/{TextToRows-ea73433d.js → TextToRows-BhtyGWPq.js} +42 -49
- flowfile/web/static/assets/{TextToRows-12afb4f4.css → TextToRows-DivDOLDx.css} +9 -9
- flowfile/web/static/assets/{ToggleSwitch-9d7b30f1.js → ToggleSwitch-B-6WzfFf.js} +2 -2
- flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-00f2580e.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-Cj8LqT-b.js} +1 -1
- flowfile/web/static/assets/{UnavailableFields-b72a2c72.js → UnavailableFields-Yf6XSqFB.js} +2 -3
- flowfile/web/static/assets/{Union-1e44f263.js → Union-CwpjeKYC.js} +20 -23
- flowfile/web/static/assets/{Unpivot-b6ad6427.css → Union-DQJcpp3-.css} +6 -6
- flowfile/web/static/assets/{Unique-a3bc6d0a.js → Unique-25v3urqH.js} +75 -74
- flowfile/web/static/assets/{Union-d6a8d7d5.css → Unpivot-Deqh1gtI.css} +6 -6
- flowfile/web/static/assets/{Unpivot-e27935fc.js → Unpivot-sYcTTXrq.js} +34 -27
- flowfile/web/static/assets/{UnpivotValidation-72497680.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-a2102880.js → api-C0LvF-0C.js} +1 -1
- flowfile/web/static/assets/{api-f75042b0.js → api-DaC83EO_.js} +1 -1
- flowfile/web/static/assets/client-C8Ygr6Gb.js +42 -0
- flowfile/web/static/assets/{dropDown-2798a109.js → dropDown-D5YXaPRR.js} +7 -12
- flowfile/web/static/assets/{fullEditor-cf7d7d93.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-d9ab70a3.js → graphic-walker.es-VrK6vdGE.js} +92305 -89751
- flowfile/web/static/assets/index-BCJxPfM5.js +6693 -0
- flowfile/web/static/assets/{index-f0a6e5a5.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-3c1757e8.js → outputCsv-BELuBiJZ.js} +2 -3
- flowfile/web/static/assets/outputCsv-CdGkv-fN.css +2581 -0
- flowfile/web/static/assets/{outputExcel-686e1f48.js → outputExcel-D0TTNM79.js} +1 -2
- flowfile/web/static/assets/{outputParquet-df28faa7.js → outputParquet-Cz9EbRHj.js} +1 -2
- flowfile/web/static/assets/{readCsv-e37eee21.js → readCsv-7bd3kUMI.js} +1 -2
- flowfile/web/static/assets/{readExcel-a13f14bb.js → readExcel-Cq8CCwIv.js} +3 -4
- flowfile/web/static/assets/{readParquet-c5244ad5.css → readParquet-CRDmBrsp.css} +4 -4
- flowfile/web/static/assets/{readParquet-344cf746.js → readParquet-DjR4mRaj.js} +4 -5
- flowfile/web/static/assets/{secrets.api-ae198c5c.js → secrets.api-C9o2KE5V.js} +1 -1
- flowfile/web/static/assets/{selectDynamic-6b4b0767.js → selectDynamic-Bl5FVsME.js} +5 -8
- flowfile/web/static/assets/useNodeSettings-dMS9zmh_.js +69 -0
- flowfile/web/static/assets/{vue-codemirror.esm-31ba0e0b.js → vue-codemirror.esm-CwaYwln0.js} +3469 -3064
- flowfile/web/static/assets/{vue-content-loader.es-4469c8ff.js → vue-content-loader.es-CMoRXo7N.js} +3 -3
- flowfile/web/static/index.html +2 -3
- {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/METADATA +2 -1
- flowfile-0.6.1.dist-info/RECORD +417 -0
- {flowfile-0.5.4.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 +158 -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/main.py +2 -4
- 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-49463352.js +0 -9
- flowfile/web/static/assets/ContextMenu-dd5f3f25.js +0 -9
- flowfile/web/static/assets/ContextMenu-f709b884.js +0 -9
- flowfile/web/static/assets/Join-28b5e18f.css +0 -109
- flowfile/web/static/assets/PopOver-862d7e28.js +0 -939
- flowfile/web/static/assets/Read-225cc63f.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-14eac1c3.js +0 -137
- flowfile/web/static/assets/genericNodeSettings-3b2507ea.css +0 -46
- flowfile/web/static/assets/index-387a6f18.js +0 -60752
- flowfile/web/static/assets/index-6b367bb5.js +0 -38
- flowfile/web/static/assets/index-e96ab018.css +0 -10466
- flowfile/web/static/assets/nodeInput-ed2ae8d7.js +0 -2
- flowfile/web/static/assets/outputCsv-b9a072af.css +0 -2499
- flowfile-0.5.4.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/{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.4.dist-info → flowfile-0.6.1.dist-info}/entry_points.txt +0 -0
- {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from flowfile_core.flowfile.flow_node.executor import InMemoryStateProvider, NodeExecutor, StateProvider
|
|
2
|
+
from flowfile_core.flowfile.flow_node.flow_node import FlowNode
|
|
3
|
+
from flowfile_core.flowfile.flow_node.models import (
|
|
4
|
+
ExecutionDecision,
|
|
5
|
+
ExecutionStrategy,
|
|
6
|
+
InvalidationReason,
|
|
7
|
+
NodeResults,
|
|
8
|
+
NodeSchemaInformation,
|
|
9
|
+
NodeStepInputs,
|
|
10
|
+
NodeStepPromise,
|
|
11
|
+
NodeStepSettings,
|
|
12
|
+
NodeStepStats,
|
|
13
|
+
)
|
|
14
|
+
from flowfile_core.flowfile.flow_node.state import NodeExecutionState, SourceFileInfo
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"FlowNode",
|
|
18
|
+
"ExecutionDecision",
|
|
19
|
+
"ExecutionStrategy",
|
|
20
|
+
"InvalidationReason",
|
|
21
|
+
"NodeResults",
|
|
22
|
+
"NodeSchemaInformation",
|
|
23
|
+
"NodeStepInputs",
|
|
24
|
+
"NodeStepPromise",
|
|
25
|
+
"NodeStepSettings",
|
|
26
|
+
"NodeStepStats",
|
|
27
|
+
"NodeExecutionState",
|
|
28
|
+
"SourceFileInfo",
|
|
29
|
+
"NodeExecutor",
|
|
30
|
+
"StateProvider",
|
|
31
|
+
"InMemoryStateProvider",
|
|
32
|
+
]
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Node execution logic - separate from node definition.
|
|
3
|
+
|
|
4
|
+
Handles the 'how to run' independently from 'what to run'.
|
|
5
|
+
Enables stateless execution by accepting external state providers.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Protocol
|
|
10
|
+
|
|
11
|
+
from flowfile_core.flowfile.flow_data_engine.subprocess_operations import (
|
|
12
|
+
results_exists,
|
|
13
|
+
)
|
|
14
|
+
from flowfile_core.flowfile.flow_node.models import (
|
|
15
|
+
ExecutionDecision,
|
|
16
|
+
ExecutionStrategy,
|
|
17
|
+
InvalidationReason,
|
|
18
|
+
)
|
|
19
|
+
from flowfile_core.flowfile.flow_node.state import NodeExecutionState, SourceFileInfo
|
|
20
|
+
from flowfile_core.schemas import schemas
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from flowfile_core.configs.flow_logger import NodeLogger
|
|
24
|
+
from flowfile_core.flowfile.flow_node.flow_node import FlowNode
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class StateProvider(Protocol):
|
|
28
|
+
"""
|
|
29
|
+
Protocol for providing/storing node state.
|
|
30
|
+
|
|
31
|
+
Implement this to store state externally (Redis, DB, etc.)
|
|
32
|
+
for stateless worker support.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def get_state(self, node_id: str | int, flow_id: str) -> NodeExecutionState:
|
|
36
|
+
"""Retrieve state for a node."""
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
def save_state(self, node_id: str | int, flow_id: str, state: NodeExecutionState) -> None:
|
|
40
|
+
"""Persist state for a node."""
|
|
41
|
+
...
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class InMemoryStateProvider:
|
|
45
|
+
"""
|
|
46
|
+
Default state provider: state lives in FlowNode._execution_state.
|
|
47
|
+
|
|
48
|
+
This maintains current behavior where state is kept in memory
|
|
49
|
+
on the node instance itself.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self, node: FlowNode):
|
|
53
|
+
self._node = node
|
|
54
|
+
|
|
55
|
+
def get_state(self, node_id: str | int, flow_id: str) -> NodeExecutionState:
|
|
56
|
+
return self._node._execution_state
|
|
57
|
+
|
|
58
|
+
def save_state(self, node_id: str | int, flow_id: str, state: NodeExecutionState) -> None:
|
|
59
|
+
# For in-memory, state is already on the node - nothing to do
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class NodeExecutor:
|
|
64
|
+
"""
|
|
65
|
+
Handles node execution logic.
|
|
66
|
+
|
|
67
|
+
Separated from FlowNode to allow:
|
|
68
|
+
- Stateless execution (state from external source)
|
|
69
|
+
- Different execution strategies
|
|
70
|
+
- Easier testing
|
|
71
|
+
- Clear separation of concerns
|
|
72
|
+
|
|
73
|
+
Performance note: This class is designed to be reused via lazy
|
|
74
|
+
instantiation on FlowNode._executor to avoid repeated object creation.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
__slots__ = ('node', 'state_provider')
|
|
78
|
+
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
node: FlowNode,
|
|
82
|
+
state_provider: StateProvider | None = None,
|
|
83
|
+
):
|
|
84
|
+
self.node = node
|
|
85
|
+
self.state_provider = state_provider or InMemoryStateProvider(node)
|
|
86
|
+
|
|
87
|
+
def execute(
|
|
88
|
+
self,
|
|
89
|
+
run_location: schemas.ExecutionLocationsLiteral,
|
|
90
|
+
reset_cache: bool = False,
|
|
91
|
+
performance_mode: bool = False,
|
|
92
|
+
retry: bool = True,
|
|
93
|
+
node_logger: NodeLogger = None,
|
|
94
|
+
optimize_for_downstream: bool = True,
|
|
95
|
+
) -> None:
|
|
96
|
+
"""
|
|
97
|
+
Main execution entry point.
|
|
98
|
+
|
|
99
|
+
This method is called when the fast-path in FlowNode.execute_node()
|
|
100
|
+
determines that full execution logic is needed.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
run_location: Where to execute ('local' or 'remote')
|
|
104
|
+
reset_cache: Force cache invalidation
|
|
105
|
+
performance_mode: Skip example data generation for speed
|
|
106
|
+
retry: Allow retry on recoverable errors
|
|
107
|
+
node_logger: Logger for this node's execution
|
|
108
|
+
optimize_for_downstream: Cache wide transforms for downstream nodes
|
|
109
|
+
"""
|
|
110
|
+
if node_logger is None:
|
|
111
|
+
raise ValueError("node_logger is required")
|
|
112
|
+
|
|
113
|
+
state = self.state_provider.get_state(self.node.node_id, self.node.parent_uuid)
|
|
114
|
+
|
|
115
|
+
# Handle explicit cache reset
|
|
116
|
+
if reset_cache:
|
|
117
|
+
self._clear_cache(state)
|
|
118
|
+
|
|
119
|
+
# Decide execution strategy
|
|
120
|
+
decision = self._decide_execution(state, run_location, performance_mode, reset_cache)
|
|
121
|
+
|
|
122
|
+
# Override for wide transforms when optimizing for downstream
|
|
123
|
+
if (decision.should_run
|
|
124
|
+
and decision.strategy == ExecutionStrategy.LOCAL_WITH_SAMPLING
|
|
125
|
+
and self.node.node_default
|
|
126
|
+
and self.node.node_default.transform_type == "wide"
|
|
127
|
+
and optimize_for_downstream
|
|
128
|
+
and run_location != "local"):
|
|
129
|
+
decision = ExecutionDecision(True, ExecutionStrategy.REMOTE, decision.reason)
|
|
130
|
+
|
|
131
|
+
if not decision.should_run:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
reason_str = decision.reason.name if decision.reason else "UNKNOWN"
|
|
135
|
+
strategy_str = decision.strategy.name
|
|
136
|
+
node_logger.info(f"Starting to run {self.node.__name__} ({reason_str} -> {strategy_str})")
|
|
137
|
+
|
|
138
|
+
# Override performance_mode when cache_results is enabled
|
|
139
|
+
# This ensures example data is generated even in Performance mode
|
|
140
|
+
effective_performance_mode = performance_mode
|
|
141
|
+
if self.node.node_settings.cache_results:
|
|
142
|
+
effective_performance_mode = False
|
|
143
|
+
|
|
144
|
+
# Prepare and execute
|
|
145
|
+
self._prepare_for_execution(state)
|
|
146
|
+
self.node.reset()
|
|
147
|
+
|
|
148
|
+
try:
|
|
149
|
+
self._execute_with_strategy(state, decision.strategy, effective_performance_mode, node_logger)
|
|
150
|
+
self._update_source_file_info(state)
|
|
151
|
+
self._sync_state_to_legacy(state)
|
|
152
|
+
self.state_provider.save_state(self.node.node_id, self.node.parent_uuid, state)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
self._handle_error(state, e, run_location, effective_performance_mode, retry, node_logger)
|
|
155
|
+
|
|
156
|
+
def _decide_execution(
|
|
157
|
+
self,
|
|
158
|
+
state: NodeExecutionState,
|
|
159
|
+
run_location: schemas.ExecutionLocationsLiteral,
|
|
160
|
+
performance_mode: bool,
|
|
161
|
+
force_refresh: bool,
|
|
162
|
+
) -> ExecutionDecision:
|
|
163
|
+
"""
|
|
164
|
+
Single source of truth for execution decisions.
|
|
165
|
+
|
|
166
|
+
Determines both WHETHER to run and HOW to run in one place.
|
|
167
|
+
"""
|
|
168
|
+
# Output nodes always run
|
|
169
|
+
if self.node.node_template.node_group == "output":
|
|
170
|
+
strategy = self._determine_strategy(run_location)
|
|
171
|
+
return ExecutionDecision(True, strategy, InvalidationReason.OUTPUT_NODE)
|
|
172
|
+
|
|
173
|
+
# Forced refresh (reset_cache=True)
|
|
174
|
+
if force_refresh:
|
|
175
|
+
strategy = self._determine_strategy(run_location)
|
|
176
|
+
return ExecutionDecision(True, strategy, InvalidationReason.FORCED_REFRESH)
|
|
177
|
+
|
|
178
|
+
# Never ran before
|
|
179
|
+
if not state.has_run_with_current_setup:
|
|
180
|
+
strategy = self._determine_strategy(run_location)
|
|
181
|
+
return ExecutionDecision(True, strategy, InvalidationReason.NEVER_RAN)
|
|
182
|
+
|
|
183
|
+
# Check if source file changed (for read nodes)
|
|
184
|
+
if self._source_file_changed(state):
|
|
185
|
+
strategy = self._determine_strategy(run_location)
|
|
186
|
+
return ExecutionDecision(True, strategy, InvalidationReason.SOURCE_FILE_CHANGED)
|
|
187
|
+
|
|
188
|
+
# Cache-enabled nodes: check if cache file is still present
|
|
189
|
+
if self.node.node_settings.cache_results:
|
|
190
|
+
if results_exists(self.node.hash):
|
|
191
|
+
return ExecutionDecision(False, ExecutionStrategy.SKIP, None)
|
|
192
|
+
strategy = self._determine_strategy(run_location)
|
|
193
|
+
return ExecutionDecision(True, strategy, InvalidationReason.CACHE_MISSING)
|
|
194
|
+
|
|
195
|
+
# Already ran with current settings → skip
|
|
196
|
+
# Results are available in memory from previous execution
|
|
197
|
+
return ExecutionDecision(False, ExecutionStrategy.SKIP, None)
|
|
198
|
+
|
|
199
|
+
def _determine_strategy(
|
|
200
|
+
self,
|
|
201
|
+
run_location: schemas.ExecutionLocationsLiteral,
|
|
202
|
+
) -> ExecutionStrategy:
|
|
203
|
+
"""Determine the execution strategy based on location and node settings.
|
|
204
|
+
|
|
205
|
+
Decision logic:
|
|
206
|
+
- local → FULL_LOCAL
|
|
207
|
+
- remote + cache_results → REMOTE (caching needs full materialization)
|
|
208
|
+
- remote + narrow transform → LOCAL_WITH_SAMPLING (fast local compute + external sampler)
|
|
209
|
+
- remote → REMOTE
|
|
210
|
+
|
|
211
|
+
Narrow transforms (e.g., select, sample, union) only operate on columns
|
|
212
|
+
without reshaping data, so they're cheap to compute locally. An external
|
|
213
|
+
sampler provides preview data for the UI.
|
|
214
|
+
|
|
215
|
+
When cache_results is enabled, the node must run fully remote so the
|
|
216
|
+
result can be materialized and stored in the cache.
|
|
217
|
+
"""
|
|
218
|
+
# Local execution mode (e.g., WASM, no worker available)
|
|
219
|
+
if run_location == "local":
|
|
220
|
+
return ExecutionStrategy.FULL_LOCAL
|
|
221
|
+
|
|
222
|
+
# Caching requires full remote execution to materialize and store results
|
|
223
|
+
if self.node.node_settings.cache_results:
|
|
224
|
+
return ExecutionStrategy.REMOTE
|
|
225
|
+
|
|
226
|
+
# Narrow transforms are lightweight column-level operations that can
|
|
227
|
+
# run locally with an external sampler for preview data
|
|
228
|
+
if (self.node.node_default is not None
|
|
229
|
+
and self.node.node_default.transform_type == "narrow"):
|
|
230
|
+
return ExecutionStrategy.LOCAL_WITH_SAMPLING
|
|
231
|
+
|
|
232
|
+
# Full remote execution for wide transforms and everything else
|
|
233
|
+
return ExecutionStrategy.REMOTE
|
|
234
|
+
|
|
235
|
+
def _execute_with_strategy(
|
|
236
|
+
self,
|
|
237
|
+
state: NodeExecutionState,
|
|
238
|
+
strategy: ExecutionStrategy,
|
|
239
|
+
performance_mode: bool,
|
|
240
|
+
node_logger: NodeLogger,
|
|
241
|
+
) -> None:
|
|
242
|
+
"""Execute using the determined strategy."""
|
|
243
|
+
match strategy:
|
|
244
|
+
case ExecutionStrategy.SKIP:
|
|
245
|
+
return
|
|
246
|
+
case ExecutionStrategy.FULL_LOCAL:
|
|
247
|
+
self._do_full_local(state, performance_mode)
|
|
248
|
+
case ExecutionStrategy.LOCAL_WITH_SAMPLING:
|
|
249
|
+
self._do_local_with_sampling(state, performance_mode, node_logger.flow_id)
|
|
250
|
+
case ExecutionStrategy.REMOTE:
|
|
251
|
+
self._do_remote(state, performance_mode, node_logger)
|
|
252
|
+
|
|
253
|
+
def _do_full_local(self, state: NodeExecutionState, performance_mode: bool) -> None:
|
|
254
|
+
"""
|
|
255
|
+
100% in-process execution.
|
|
256
|
+
|
|
257
|
+
Used for WASM environments or when no external worker is available.
|
|
258
|
+
"""
|
|
259
|
+
self.node._do_execute_full_local(performance_mode)
|
|
260
|
+
if not performance_mode:
|
|
261
|
+
state.mark_successful()
|
|
262
|
+
if self.node.results.resulting_data is not None:
|
|
263
|
+
state.result_schema = self.node.results.resulting_data.schema
|
|
264
|
+
|
|
265
|
+
def _do_local_with_sampling(
|
|
266
|
+
self,
|
|
267
|
+
state: NodeExecutionState,
|
|
268
|
+
performance_mode: bool,
|
|
269
|
+
flow_id: int
|
|
270
|
+
) -> None:
|
|
271
|
+
"""
|
|
272
|
+
In-process execution with external sampler for preview data.
|
|
273
|
+
|
|
274
|
+
The main computation runs locally, but sample data is generated
|
|
275
|
+
via an external process for the UI preview.
|
|
276
|
+
"""
|
|
277
|
+
self.node._do_execute_local_with_sampling(performance_mode, flow_id)
|
|
278
|
+
if self.node.results.resulting_data is not None:
|
|
279
|
+
state.result_schema = self.node.results.resulting_data.schema
|
|
280
|
+
if self.node.results.errors is None and not self.node.node_stats.is_canceled:
|
|
281
|
+
state.mark_successful()
|
|
282
|
+
|
|
283
|
+
def _do_remote(
|
|
284
|
+
self,
|
|
285
|
+
state: NodeExecutionState,
|
|
286
|
+
performance_mode: bool,
|
|
287
|
+
node_logger: NodeLogger
|
|
288
|
+
) -> None:
|
|
289
|
+
"""
|
|
290
|
+
Full remote worker execution.
|
|
291
|
+
|
|
292
|
+
Computation is offloaded to an external worker process.
|
|
293
|
+
"""
|
|
294
|
+
self.node._do_execute_remote(performance_mode, node_logger)
|
|
295
|
+
if self.node.results.resulting_data is not None:
|
|
296
|
+
state.result_schema = self.node.results.resulting_data.schema
|
|
297
|
+
state.mark_successful()
|
|
298
|
+
|
|
299
|
+
def _source_file_changed(self, state: NodeExecutionState) -> bool:
|
|
300
|
+
"""
|
|
301
|
+
Check if source file has changed since last successful run.
|
|
302
|
+
|
|
303
|
+
Only applicable to read nodes. Returns False for other node types.
|
|
304
|
+
"""
|
|
305
|
+
if self.node.node_type != "read":
|
|
306
|
+
return False
|
|
307
|
+
|
|
308
|
+
path = self._get_source_path()
|
|
309
|
+
if not path:
|
|
310
|
+
return False
|
|
311
|
+
|
|
312
|
+
# First time - no previous info to compare
|
|
313
|
+
if state.source_file_info is None:
|
|
314
|
+
return False
|
|
315
|
+
|
|
316
|
+
return state.source_file_info.has_changed()
|
|
317
|
+
|
|
318
|
+
def _get_source_path(self) -> str | None:
|
|
319
|
+
"""Get the source file path for read nodes."""
|
|
320
|
+
setting_input = self.node.setting_input
|
|
321
|
+
if not hasattr(setting_input, 'received_file') or not setting_input.received_file:
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
rf = setting_input.received_file
|
|
325
|
+
# Prefer absolute path if available
|
|
326
|
+
if hasattr(rf, 'abs_file_path') and rf.abs_file_path:
|
|
327
|
+
return rf.abs_file_path
|
|
328
|
+
return rf.path if hasattr(rf, 'path') else None
|
|
329
|
+
|
|
330
|
+
def _update_source_file_info(self, state: NodeExecutionState) -> None:
|
|
331
|
+
"""Update source file tracking after successful execution."""
|
|
332
|
+
if self.node.node_type != "read":
|
|
333
|
+
return
|
|
334
|
+
|
|
335
|
+
path = self._get_source_path()
|
|
336
|
+
if path:
|
|
337
|
+
state.source_file_info = SourceFileInfo.from_path(path)
|
|
338
|
+
|
|
339
|
+
def _prepare_for_execution(self, state: NodeExecutionState) -> None:
|
|
340
|
+
"""Prepare node state before execution."""
|
|
341
|
+
self.node.clear_table_example()
|
|
342
|
+
state.reset_results_only()
|
|
343
|
+
self.node.results.errors = None
|
|
344
|
+
self.node.results.resulting_data = None
|
|
345
|
+
self.node.results.example_data = None
|
|
346
|
+
|
|
347
|
+
def _clear_cache(self, state: NodeExecutionState) -> None:
|
|
348
|
+
"""Clear cached results."""
|
|
349
|
+
self.node.remove_cache()
|
|
350
|
+
state.has_run_with_current_setup = False
|
|
351
|
+
state.has_completed_last_run = False
|
|
352
|
+
|
|
353
|
+
def _sync_state_to_legacy(self, state: NodeExecutionState) -> None:
|
|
354
|
+
"""Sync _execution_state to legacy node_stats for backwards compatibility."""
|
|
355
|
+
self.node.node_stats._has_run_with_current_setup = state.has_run_with_current_setup
|
|
356
|
+
self.node.node_stats.has_completed_last_run = state.has_completed_last_run
|
|
357
|
+
self.node.node_stats.is_canceled = state.is_canceled
|
|
358
|
+
self.node.node_stats.error = state.error
|
|
359
|
+
|
|
360
|
+
def _handle_error(
|
|
361
|
+
self,
|
|
362
|
+
state: NodeExecutionState,
|
|
363
|
+
error: Exception,
|
|
364
|
+
run_location: schemas.ExecutionLocationsLiteral,
|
|
365
|
+
performance_mode: bool,
|
|
366
|
+
retry: bool,
|
|
367
|
+
node_logger: NodeLogger,
|
|
368
|
+
) -> None:
|
|
369
|
+
"""Handle execution errors with retry logic."""
|
|
370
|
+
error_str = str(error)
|
|
371
|
+
state.mark_failed(error_str)
|
|
372
|
+
self._sync_state_to_legacy(state)
|
|
373
|
+
self.node.results.errors = error_str
|
|
374
|
+
|
|
375
|
+
# Retry on missing file errors (upstream cache was cleared)
|
|
376
|
+
if "No such file or directory (os error" in error_str and retry:
|
|
377
|
+
node_logger.warning("Input file missing, retrying upstream nodes...")
|
|
378
|
+
for node_input in self.node.node_inputs.get_all_inputs():
|
|
379
|
+
# Recursively execute upstream nodes
|
|
380
|
+
node_input.execute_node(
|
|
381
|
+
run_location=run_location,
|
|
382
|
+
performance_mode=performance_mode,
|
|
383
|
+
retry=True,
|
|
384
|
+
reset_cache=True,
|
|
385
|
+
node_logger=node_logger,
|
|
386
|
+
)
|
|
387
|
+
# Retry this node once (no further retries)
|
|
388
|
+
self.execute(
|
|
389
|
+
run_location=run_location,
|
|
390
|
+
performance_mode=performance_mode,
|
|
391
|
+
retry=False,
|
|
392
|
+
node_logger=node_logger,
|
|
393
|
+
)
|
|
394
|
+
return
|
|
395
|
+
|
|
396
|
+
# Log appropriate error message
|
|
397
|
+
if "Connection refused" in error_str and "/submit_query/" in error_str:
|
|
398
|
+
node_logger.warning(
|
|
399
|
+
"Could not connect to remote worker. "
|
|
400
|
+
"Ensure the worker process is running, or change settings to local execution."
|
|
401
|
+
)
|
|
402
|
+
node_logger.error("Remote worker connection refused")
|
|
403
|
+
else:
|
|
404
|
+
node_logger.error(f"Error running node: {error}")
|