flwr-nightly 1.23.0.dev20250930__py3-none-any.whl → 1.26.0.dev20260121__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.
- flwr/__init__.py +17 -6
- flwr/app/__init__.py +4 -1
- flwr/app/error.py +2 -2
- flwr/app/exception.py +3 -3
- flwr/app/message_type.py +29 -0
- flwr/app/metadata.py +5 -2
- flwr/app/user_config.py +19 -0
- flwr/cli/app.py +62 -9
- flwr/cli/{new/templates/app/code → app_cmd}/__init__.py +9 -1
- flwr/cli/app_cmd/publish.py +285 -0
- flwr/cli/app_cmd/review.py +262 -0
- flwr/cli/auth_plugin/__init__.py +13 -6
- flwr/cli/auth_plugin/auth_plugin.py +26 -15
- flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
- flwr/cli/auth_plugin/oidc_cli_plugin.py +52 -32
- flwr/cli/build.py +166 -53
- flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +27 -10
- flwr/cli/config/__init__.py +21 -0
- flwr/cli/config/ls.py +104 -0
- flwr/cli/config_migration.py +300 -0
- flwr/cli/config_utils.py +154 -13
- flwr/cli/constant.py +67 -0
- flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +8 -1
- flwr/cli/federation/ls.py +361 -0
- flwr/cli/flower_config.py +447 -0
- flwr/cli/install.py +91 -13
- flwr/cli/log.py +65 -36
- flwr/cli/login/login.py +41 -27
- flwr/cli/ls.py +232 -158
- flwr/cli/new/new.py +188 -244
- flwr/cli/pull.py +25 -34
- flwr/cli/run/run.py +106 -74
- flwr/cli/run_utils.py +148 -0
- flwr/cli/stop.py +46 -37
- flwr/cli/supernode/__init__.py +25 -0
- flwr/cli/supernode/ls.py +273 -0
- flwr/cli/supernode/register.py +190 -0
- flwr/cli/supernode/unregister.py +140 -0
- flwr/cli/typing.py +211 -0
- flwr/cli/utils.py +428 -80
- flwr/client/__init__.py +2 -1
- flwr/client/dpfedavg_numpy_client.py +4 -1
- flwr/client/grpc_adapter_client/connection.py +14 -17
- flwr/client/grpc_rere_client/connection.py +73 -43
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -15
- flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
- flwr/client/message_handler/message_handler.py +4 -3
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/localdp_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +11 -9
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/connection.py +99 -54
- flwr/client/run_info_store.py +6 -6
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +1 -2
- flwr/{client → clientapp}/client_app.py +11 -11
- flwr/clientapp/mod/centraldp_mods.py +16 -17
- flwr/clientapp/mod/localdp_mod.py +8 -9
- flwr/clientapp/typing.py +1 -1
- flwr/{client/clientapp → clientapp}/utils.py +4 -4
- flwr/common/__init__.py +3 -2
- flwr/common/args.py +3 -4
- flwr/common/config.py +15 -17
- flwr/common/constant.py +56 -28
- flwr/common/context.py +2 -1
- flwr/common/differential_privacy.py +3 -4
- flwr/common/event_log_plugin/event_log_plugin.py +3 -4
- flwr/common/exit/exit.py +16 -3
- flwr/common/exit/exit_code.py +39 -10
- flwr/common/exit/exit_handler.py +6 -2
- flwr/common/exit/signal_handler.py +5 -5
- flwr/common/grpc.py +8 -7
- flwr/common/inflatable_protobuf_utils.py +1 -1
- flwr/common/inflatable_utils.py +48 -31
- flwr/common/logger.py +19 -19
- flwr/common/message.py +5 -5
- flwr/common/object_ref.py +7 -7
- flwr/common/record/array.py +6 -6
- flwr/common/record/arrayrecord.py +18 -21
- flwr/common/record/configrecord.py +3 -3
- flwr/common/record/recorddict.py +5 -5
- flwr/common/record/typeddict.py +9 -2
- flwr/common/recorddict_compat.py +7 -10
- flwr/common/retry_invoker.py +20 -20
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +8 -5
- flwr/common/serde.py +22 -11
- flwr/common/serde_utils.py +2 -2
- flwr/common/telemetry.py +10 -6
- flwr/common/typing.py +65 -44
- flwr/compat/client/app.py +45 -47
- flwr/compat/client/grpc_client/connection.py +15 -14
- flwr/compat/common/constant.py +29 -0
- flwr/compat/server/app.py +6 -7
- flwr/proto/appio_pb2.py +13 -3
- flwr/proto/appio_pb2.pyi +134 -65
- flwr/proto/appio_pb2_grpc.py +20 -0
- flwr/proto/appio_pb2_grpc.pyi +27 -0
- flwr/proto/clientappio_pb2.py +17 -7
- flwr/proto/clientappio_pb2.pyi +15 -0
- flwr/proto/clientappio_pb2_grpc.py +206 -40
- flwr/proto/clientappio_pb2_grpc.pyi +168 -53
- flwr/proto/control_pb2.py +72 -40
- flwr/proto/control_pb2.pyi +319 -87
- flwr/proto/control_pb2_grpc.py +339 -28
- flwr/proto/control_pb2_grpc.pyi +209 -37
- flwr/proto/error_pb2.py +13 -3
- flwr/proto/error_pb2.pyi +24 -6
- flwr/proto/error_pb2_grpc.py +20 -0
- flwr/proto/error_pb2_grpc.pyi +27 -0
- flwr/proto/fab_pb2.py +24 -10
- flwr/proto/fab_pb2.pyi +68 -20
- flwr/proto/fab_pb2_grpc.py +20 -0
- flwr/proto/fab_pb2_grpc.pyi +27 -0
- flwr/proto/federation_pb2.py +38 -0
- flwr/proto/federation_pb2.pyi +56 -0
- flwr/proto/federation_pb2_grpc.py +24 -0
- flwr/proto/federation_pb2_grpc.pyi +31 -0
- flwr/proto/fleet_pb2.py +45 -27
- flwr/proto/fleet_pb2.pyi +190 -70
- flwr/proto/fleet_pb2_grpc.py +277 -66
- flwr/proto/fleet_pb2_grpc.pyi +201 -55
- flwr/proto/grpcadapter_pb2.py +14 -4
- flwr/proto/grpcadapter_pb2.pyi +38 -16
- flwr/proto/grpcadapter_pb2_grpc.py +35 -4
- flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
- flwr/proto/heartbeat_pb2.py +17 -7
- flwr/proto/heartbeat_pb2.pyi +51 -22
- flwr/proto/heartbeat_pb2_grpc.py +20 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
- flwr/proto/log_pb2.py +13 -3
- flwr/proto/log_pb2.pyi +34 -11
- flwr/proto/log_pb2_grpc.py +20 -0
- flwr/proto/log_pb2_grpc.pyi +27 -0
- flwr/proto/message_pb2.py +15 -5
- flwr/proto/message_pb2.pyi +154 -86
- flwr/proto/message_pb2_grpc.py +20 -0
- flwr/proto/message_pb2_grpc.pyi +27 -0
- flwr/proto/node_pb2.py +16 -4
- flwr/proto/node_pb2.pyi +77 -4
- flwr/proto/node_pb2_grpc.py +20 -0
- flwr/proto/node_pb2_grpc.pyi +27 -0
- flwr/proto/recorddict_pb2.py +13 -3
- flwr/proto/recorddict_pb2.pyi +184 -107
- flwr/proto/recorddict_pb2_grpc.py +20 -0
- flwr/proto/recorddict_pb2_grpc.pyi +27 -0
- flwr/proto/run_pb2.py +40 -31
- flwr/proto/run_pb2.pyi +158 -84
- flwr/proto/run_pb2_grpc.py +20 -0
- flwr/proto/run_pb2_grpc.pyi +27 -0
- flwr/proto/serverappio_pb2.py +13 -3
- flwr/proto/serverappio_pb2.pyi +32 -8
- flwr/proto/serverappio_pb2_grpc.py +246 -65
- flwr/proto/serverappio_pb2_grpc.pyi +221 -85
- flwr/proto/simulationio_pb2.py +16 -8
- flwr/proto/simulationio_pb2.pyi +15 -0
- flwr/proto/simulationio_pb2_grpc.py +162 -41
- flwr/proto/simulationio_pb2_grpc.pyi +149 -55
- flwr/proto/transport_pb2.py +20 -10
- flwr/proto/transport_pb2.pyi +249 -160
- flwr/proto/transport_pb2_grpc.py +35 -4
- flwr/proto/transport_pb2_grpc.pyi +38 -8
- flwr/server/app.py +175 -128
- flwr/server/client_manager.py +4 -5
- flwr/server/client_proxy.py +10 -11
- flwr/server/compat/app.py +4 -5
- flwr/server/compat/app_utils.py +2 -1
- flwr/server/compat/grid_client_proxy.py +12 -13
- flwr/server/compat/legacy_context.py +3 -4
- flwr/server/fleet_event_log_interceptor.py +2 -1
- flwr/server/grid/grid.py +2 -3
- flwr/server/grid/grpc_grid.py +12 -10
- flwr/server/grid/inmemory_grid.py +4 -4
- flwr/server/run_serverapp.py +2 -3
- flwr/server/server.py +34 -39
- flwr/server/server_app.py +7 -8
- flwr/server/server_config.py +1 -2
- flwr/server/serverapp/app.py +34 -28
- flwr/server/serverapp_components.py +4 -5
- flwr/server/strategy/aggregate.py +9 -8
- flwr/server/strategy/bulyan.py +13 -11
- flwr/server/strategy/dp_adaptive_clipping.py +16 -20
- flwr/server/strategy/dp_fixed_clipping.py +12 -17
- flwr/server/strategy/dpfedavg_adaptive.py +3 -4
- flwr/server/strategy/dpfedavg_fixed.py +6 -10
- flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
- flwr/server/strategy/fedadagrad.py +18 -14
- flwr/server/strategy/fedadam.py +16 -14
- flwr/server/strategy/fedavg.py +16 -17
- flwr/server/strategy/fedavg_android.py +15 -15
- flwr/server/strategy/fedavgm.py +21 -18
- flwr/server/strategy/fedmedian.py +2 -3
- flwr/server/strategy/fedopt.py +11 -10
- flwr/server/strategy/fedprox.py +10 -9
- flwr/server/strategy/fedtrimmedavg.py +12 -11
- flwr/server/strategy/fedxgb_bagging.py +13 -11
- flwr/server/strategy/fedxgb_cyclic.py +6 -6
- flwr/server/strategy/fedxgb_nn_avg.py +4 -4
- flwr/server/strategy/fedyogi.py +16 -14
- flwr/server/strategy/krum.py +12 -11
- flwr/server/strategy/qfedavg.py +16 -15
- flwr/server/strategy/strategy.py +6 -9
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -9
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +136 -42
- flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +28 -50
- flwr/server/superlink/fleet/message_handler/message_handler.py +141 -51
- flwr/server/superlink/fleet/rest_rere/rest_api.py +54 -33
- flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
- flwr/server/superlink/fleet/vce/backend/raybackend.py +6 -6
- flwr/server/superlink/fleet/vce/vce_api.py +32 -13
- flwr/server/superlink/linkstate/__init__.py +2 -0
- flwr/server/superlink/linkstate/in_memory_linkstate.py +293 -208
- flwr/server/superlink/linkstate/linkstate.py +176 -64
- flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
- flwr/server/superlink/linkstate/sql_linkstate.py +221 -0
- flwr/server/superlink/linkstate/sqlite_linkstate.py +743 -648
- flwr/server/superlink/linkstate/utils.py +11 -62
- flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +28 -23
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
- flwr/server/superlink/simulation/simulationio_servicer.py +19 -14
- flwr/server/superlink/utils.py +4 -6
- flwr/server/typing.py +1 -1
- flwr/server/utils/tensorboard.py +15 -8
- flwr/server/utils/validator.py +2 -3
- flwr/server/workflow/default_workflows.py +7 -6
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +13 -11
- flwr/serverapp/strategy/bulyan.py +16 -15
- flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
- flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
- flwr/serverapp/strategy/fedadagrad.py +10 -11
- flwr/serverapp/strategy/fedadam.py +10 -11
- flwr/serverapp/strategy/fedavg.py +10 -11
- flwr/serverapp/strategy/fedavgm.py +17 -16
- flwr/serverapp/strategy/fedmedian.py +2 -2
- flwr/serverapp/strategy/fedopt.py +10 -11
- flwr/serverapp/strategy/fedprox.py +7 -8
- flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
- flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
- flwr/serverapp/strategy/fedxgb_cyclic.py +10 -10
- flwr/serverapp/strategy/fedyogi.py +9 -11
- flwr/serverapp/strategy/krum.py +7 -7
- flwr/serverapp/strategy/multikrum.py +9 -9
- flwr/serverapp/strategy/qfedavg.py +17 -16
- flwr/serverapp/strategy/strategy.py +6 -9
- flwr/serverapp/strategy/strategy_utils.py +7 -8
- flwr/simulation/app.py +46 -42
- flwr/simulation/legacy_app.py +12 -12
- flwr/simulation/ray_transport/ray_actor.py +11 -12
- flwr/simulation/ray_transport/ray_client_proxy.py +14 -19
- flwr/simulation/run_simulation.py +46 -44
- flwr/simulation/simulationio_connection.py +4 -4
- flwr/{common → supercore}/address.py +1 -37
- flwr/supercore/cli/flower_superexec.py +3 -4
- flwr/supercore/constant.py +69 -0
- flwr/supercore/corestate/corestate.py +24 -3
- flwr/supercore/corestate/in_memory_corestate.py +138 -0
- flwr/supercore/corestate/sql_corestate.py +153 -0
- flwr/supercore/corestate/sqlite_corestate.py +157 -0
- flwr/supercore/credential_store/__init__.py +33 -0
- flwr/supercore/credential_store/credential_store.py +34 -0
- flwr/supercore/credential_store/file_credential_store.py +76 -0
- flwr/{common → supercore}/date.py +0 -11
- flwr/supercore/ffs/disk_ffs.py +1 -2
- flwr/supercore/ffs/ffs.py +1 -2
- flwr/supercore/ffs/ffs_factory.py +1 -2
- flwr/{common → supercore}/heartbeat.py +20 -25
- flwr/supercore/object_store/in_memory_object_store.py +1 -6
- flwr/supercore/object_store/object_store.py +1 -2
- flwr/supercore/object_store/object_store_factory.py +27 -8
- flwr/supercore/object_store/sqlite_object_store.py +253 -0
- flwr/{cli/new/templates/app → supercore/primitives}/__init__.py +1 -1
- flwr/supercore/primitives/asymmetric.py +117 -0
- flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
- flwr/supercore/sql_mixin.py +292 -0
- flwr/supercore/sqlite_mixin.py +156 -0
- flwr/{client/clientapp → supercore/state}/__init__.py +2 -2
- flwr/supercore/state/schema/README.md +125 -0
- flwr/{cli/new/templates → supercore/state/schema}/__init__.py +2 -2
- flwr/supercore/state/schema/corestate_tables.py +36 -0
- flwr/supercore/state/schema/linkstate_tables.py +152 -0
- flwr/supercore/state/schema/objectstore_tables.py +90 -0
- flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
- flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
- flwr/supercore/superexec/run_superexec.py +9 -13
- flwr/supercore/utils.py +224 -0
- flwr/superlink/artifact_provider/artifact_provider.py +1 -2
- flwr/superlink/auth_plugin/__init__.py +5 -2
- flwr/superlink/auth_plugin/auth_plugin.py +20 -19
- flwr/superlink/auth_plugin/noop_auth_plugin.py +84 -0
- flwr/superlink/federation/__init__.py +24 -0
- flwr/superlink/federation/federation_manager.py +64 -0
- flwr/superlink/federation/noop_federation_manager.py +71 -0
- flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +41 -32
- flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
- flwr/superlink/servicer/control/control_grpc.py +20 -17
- flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
- flwr/superlink/servicer/control/control_servicer.py +328 -68
- flwr/supernode/cli/flower_supernode.py +74 -26
- flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
- flwr/supernode/nodestate/nodestate.py +52 -8
- flwr/supernode/nodestate/nodestate_factory.py +7 -4
- flwr/supernode/runtime/run_clientapp.py +43 -24
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +48 -10
- flwr/supernode/start_client_internal.py +185 -57
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/METADATA +10 -11
- flwr_nightly-1.26.0.dev20260121.dist-info/RECORD +411 -0
- flwr/cli/new/templates/app/.gitignore.tpl +0 -163
- flwr/cli/new/templates/app/LICENSE.tpl +0 -202
- flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
- flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
- flwr/cli/new/templates/app/README.md.tpl +0 -37
- flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
- flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
- flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
- flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
- flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
- flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
- flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
- flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
- flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
- flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
- flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
- flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
- flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
- flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
- flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
- flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
- flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
- flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
- flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
- flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
- flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
- flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
- flwr/common/pyproject.py +0 -42
- flwr/supercore/object_store/utils.py +0 -43
- flwr_nightly-1.23.0.dev20250930.dist-info/RECORD +0 -429
- /flwr/{common → supercore}/version.py +0 -0
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/entry_points.txt +0 -0
|
@@ -35,7 +35,7 @@ from flwr.common.typing import Fab, Run
|
|
|
35
35
|
|
|
36
36
|
# pylint: disable=E0611
|
|
37
37
|
from flwr.proto import clientappio_pb2_grpc
|
|
38
|
-
from flwr.proto.appio_pb2 import (
|
|
38
|
+
from flwr.proto.appio_pb2 import (
|
|
39
39
|
ListAppsToLaunchRequest,
|
|
40
40
|
ListAppsToLaunchResponse,
|
|
41
41
|
PullAppInputsRequest,
|
|
@@ -49,6 +49,7 @@ from flwr.proto.appio_pb2 import ( # pylint: disable=E0401
|
|
|
49
49
|
RequestTokenRequest,
|
|
50
50
|
RequestTokenResponse,
|
|
51
51
|
)
|
|
52
|
+
from flwr.proto.heartbeat_pb2 import SendAppHeartbeatRequest, SendAppHeartbeatResponse
|
|
52
53
|
from flwr.proto.message_pb2 import (
|
|
53
54
|
ConfirmMessageReceivedRequest,
|
|
54
55
|
ConfirmMessageReceivedResponse,
|
|
@@ -57,12 +58,11 @@ from flwr.proto.message_pb2 import (
|
|
|
57
58
|
PushObjectRequest,
|
|
58
59
|
PushObjectResponse,
|
|
59
60
|
)
|
|
60
|
-
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse
|
|
61
|
+
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse
|
|
61
62
|
|
|
62
63
|
# pylint: disable=E0601
|
|
63
64
|
from flwr.supercore.ffs import FfsFactory
|
|
64
65
|
from flwr.supercore.object_store import NoObjectInStoreError, ObjectStoreFactory
|
|
65
|
-
from flwr.supercore.object_store.utils import store_mapping_and_register_objects
|
|
66
66
|
from flwr.supernode.nodestate import NodeStateFactory
|
|
67
67
|
|
|
68
68
|
|
|
@@ -151,7 +151,24 @@ class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
|
|
|
151
151
|
# Retrieve context, run and fab for this run
|
|
152
152
|
context = cast(Context, state.get_context(run_id))
|
|
153
153
|
run = cast(Run, state.get_run(run_id))
|
|
154
|
-
|
|
154
|
+
|
|
155
|
+
# Retrieve FAB from FFS
|
|
156
|
+
if result := ffs.get(run.fab_hash):
|
|
157
|
+
content, verifications = result
|
|
158
|
+
log(
|
|
159
|
+
DEBUG,
|
|
160
|
+
"Retrieved FAB: hash=%s, content_len=%d, verifications=%s",
|
|
161
|
+
run.fab_hash,
|
|
162
|
+
len(content),
|
|
163
|
+
verifications,
|
|
164
|
+
)
|
|
165
|
+
fab = Fab(run.fab_hash, content, verifications)
|
|
166
|
+
else:
|
|
167
|
+
context.abort(
|
|
168
|
+
grpc.StatusCode.NOT_FOUND,
|
|
169
|
+
f"FAB with hash {run.fab_hash} not found in FFS.",
|
|
170
|
+
)
|
|
171
|
+
raise RuntimeError("This line should never be reached.")
|
|
155
172
|
|
|
156
173
|
return PullAppInputsResponse(
|
|
157
174
|
context=context_to_proto(context),
|
|
@@ -206,6 +223,9 @@ class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
|
|
|
206
223
|
# Retrieve message for this run
|
|
207
224
|
message = state.get_messages(run_ids=[run_id], is_reply=False)[0]
|
|
208
225
|
|
|
226
|
+
# Record message processing start time
|
|
227
|
+
state.record_message_processing_start(message_id=message.metadata.message_id)
|
|
228
|
+
|
|
209
229
|
# Retrieve the object tree for the message
|
|
210
230
|
object_tree = store.get_object_tree(message.metadata.message_id)
|
|
211
231
|
|
|
@@ -231,19 +251,37 @@ class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
|
|
|
231
251
|
)
|
|
232
252
|
raise RuntimeError("This line should never be reached.")
|
|
233
253
|
|
|
234
|
-
#
|
|
235
|
-
state.
|
|
254
|
+
# Record message processing end time
|
|
255
|
+
state.record_message_processing_end(
|
|
256
|
+
message_id=request.messages_list[0].metadata.reply_to_message_id
|
|
257
|
+
)
|
|
236
258
|
|
|
237
259
|
# Store Message object to descendants mapping and preregister objects
|
|
238
|
-
objects_to_push =
|
|
260
|
+
objects_to_push: set[str] = set()
|
|
261
|
+
for object_tree in request.message_object_trees:
|
|
262
|
+
objects_to_push |= set(store.preregister(run_id, object_tree))
|
|
239
263
|
|
|
264
|
+
# Save the message to the state
|
|
265
|
+
state.store_message(message_from_proto(request.messages_list[0]))
|
|
240
266
|
return PushAppMessagesResponse(objects_to_push=objects_to_push)
|
|
241
267
|
|
|
268
|
+
def SendAppHeartbeat(
|
|
269
|
+
self, request: SendAppHeartbeatRequest, context: grpc.ServicerContext
|
|
270
|
+
) -> SendAppHeartbeatResponse:
|
|
271
|
+
"""Handle a heartbeat from an app process."""
|
|
272
|
+
log(DEBUG, "ClientAppIoServicer.SendAppHeartbeat")
|
|
273
|
+
# Initialize state
|
|
274
|
+
state = self.state_factory.state()
|
|
275
|
+
|
|
276
|
+
# Acknowledge the heartbeat
|
|
277
|
+
success = state.acknowledge_app_heartbeat(request.token)
|
|
278
|
+
return SendAppHeartbeatResponse(success=success)
|
|
279
|
+
|
|
242
280
|
def PushObject(
|
|
243
281
|
self, request: PushObjectRequest, context: grpc.ServicerContext
|
|
244
282
|
) -> PushObjectResponse:
|
|
245
283
|
"""Push an object to the ObjectStore."""
|
|
246
|
-
log(DEBUG, "
|
|
284
|
+
log(DEBUG, "ClientAppIoServicer.PushObject")
|
|
247
285
|
|
|
248
286
|
# Init state and store
|
|
249
287
|
store = self.objectstore_factory.store()
|
|
@@ -265,7 +303,7 @@ class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
|
|
|
265
303
|
self, request: PullObjectRequest, context: grpc.ServicerContext
|
|
266
304
|
) -> PullObjectResponse:
|
|
267
305
|
"""Pull an object from the ObjectStore."""
|
|
268
|
-
log(DEBUG, "
|
|
306
|
+
log(DEBUG, "ClientAppIoServicer.PullObject")
|
|
269
307
|
|
|
270
308
|
# Init state and store
|
|
271
309
|
store = self.objectstore_factory.store()
|
|
@@ -285,7 +323,7 @@ class ClientAppIoServicer(clientappio_pb2_grpc.ClientAppIoServicer):
|
|
|
285
323
|
self, request: ConfirmMessageReceivedRequest, context: grpc.ServicerContext
|
|
286
324
|
) -> ConfirmMessageReceivedResponse:
|
|
287
325
|
"""Confirm message received."""
|
|
288
|
-
log(DEBUG, "
|
|
326
|
+
log(DEBUG, "ClientAppIoServicer.ConfirmMessageReceived")
|
|
289
327
|
|
|
290
328
|
# Init state and store
|
|
291
329
|
store = self.objectstore_factory.store()
|
|
@@ -15,24 +15,27 @@
|
|
|
15
15
|
"""Main loop for Flower SuperNode."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
import hashlib
|
|
19
|
+
import json
|
|
18
20
|
import os
|
|
19
21
|
import subprocess
|
|
20
22
|
import time
|
|
21
|
-
from collections.abc import Iterator
|
|
23
|
+
from collections.abc import Callable, Iterator
|
|
22
24
|
from contextlib import contextmanager
|
|
23
25
|
from functools import partial
|
|
24
|
-
from logging import INFO
|
|
26
|
+
from logging import ERROR, INFO, WARN
|
|
25
27
|
from pathlib import Path
|
|
26
|
-
from typing import
|
|
28
|
+
from typing import cast
|
|
27
29
|
|
|
28
30
|
import grpc
|
|
29
|
-
from cryptography.hazmat.primitives.asymmetric import ec
|
|
31
|
+
from cryptography.hazmat.primitives.asymmetric import ec, ed25519
|
|
32
|
+
from cryptography.hazmat.primitives.serialization.ssh import load_ssh_public_key
|
|
30
33
|
from grpc import RpcError
|
|
31
34
|
|
|
35
|
+
from flwr.app.user_config import UserConfig
|
|
32
36
|
from flwr.client.grpc_adapter_client.connection import grpc_adapter
|
|
33
37
|
from flwr.client.grpc_rere_client.connection import grpc_request_response
|
|
34
|
-
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, Message, RecordDict
|
|
35
|
-
from flwr.common.address import parse_address
|
|
38
|
+
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, Error, Message, RecordDict
|
|
36
39
|
from flwr.common.config import get_flwr_dir, get_fused_config_from_fab
|
|
37
40
|
from flwr.common.constant import (
|
|
38
41
|
CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
@@ -41,11 +44,17 @@ from flwr.common.constant import (
|
|
|
41
44
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
42
45
|
TRANSPORT_TYPE_REST,
|
|
43
46
|
TRANSPORT_TYPES,
|
|
47
|
+
ErrorCode,
|
|
44
48
|
ExecPluginType,
|
|
45
49
|
)
|
|
46
50
|
from flwr.common.exit import ExitCode, flwr_exit, register_signal_handlers
|
|
47
51
|
from flwr.common.grpc import generic_create_grpc_server
|
|
48
|
-
from flwr.common.inflatable import
|
|
52
|
+
from flwr.common.inflatable import (
|
|
53
|
+
get_all_nested_objects,
|
|
54
|
+
get_object_tree,
|
|
55
|
+
iterate_object_tree,
|
|
56
|
+
no_object_id_recompute,
|
|
57
|
+
)
|
|
49
58
|
from flwr.common.inflatable_utils import (
|
|
50
59
|
pull_objects,
|
|
51
60
|
push_object_contents_from_iterable,
|
|
@@ -53,17 +62,26 @@ from flwr.common.inflatable_utils import (
|
|
|
53
62
|
from flwr.common.logger import log
|
|
54
63
|
from flwr.common.retry_invoker import RetryInvoker, _make_simple_grpc_retry_invoker
|
|
55
64
|
from flwr.common.telemetry import EventType
|
|
56
|
-
from flwr.common.typing import Fab, Run, RunNotRunningException
|
|
65
|
+
from flwr.common.typing import Fab, Run, RunNotRunningException
|
|
57
66
|
from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
|
|
58
67
|
from flwr.proto.message_pb2 import ObjectTree # pylint: disable=E0611
|
|
68
|
+
from flwr.supercore.address import parse_address
|
|
59
69
|
from flwr.supercore.ffs import Ffs, FfsFactory
|
|
60
70
|
from flwr.supercore.grpc_health import run_health_server_grpc_no_tls
|
|
61
71
|
from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
|
|
72
|
+
from flwr.supercore.primitives.asymmetric_ed25519 import (
|
|
73
|
+
create_message_to_sign,
|
|
74
|
+
decode_base64url,
|
|
75
|
+
verify_signature,
|
|
76
|
+
)
|
|
77
|
+
from flwr.supercore.version import package_version
|
|
62
78
|
from flwr.supernode.nodestate import NodeState, NodeStateFactory
|
|
63
79
|
from flwr.supernode.servicer.clientappio import ClientAppIoServicer
|
|
64
80
|
|
|
65
81
|
DEFAULT_FFS_DIR = get_flwr_dir() / "supernode" / "ffs"
|
|
66
82
|
|
|
83
|
+
FAB_VERIFICATION_ERROR = Error(ErrorCode.INVALID_FAB, "The FAB could not be verified.")
|
|
84
|
+
|
|
67
85
|
|
|
68
86
|
# pylint: disable=import-outside-toplevel
|
|
69
87
|
# pylint: disable=too-many-branches
|
|
@@ -74,18 +92,19 @@ def start_client_internal(
|
|
|
74
92
|
*,
|
|
75
93
|
server_address: str,
|
|
76
94
|
node_config: UserConfig,
|
|
77
|
-
root_certificates:
|
|
78
|
-
insecure:
|
|
95
|
+
root_certificates: bytes | str | None = None,
|
|
96
|
+
insecure: bool | None = None,
|
|
79
97
|
transport: str,
|
|
80
|
-
authentication_keys:
|
|
81
|
-
tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
|
|
82
|
-
|
|
83
|
-
max_retries:
|
|
84
|
-
max_wait_time:
|
|
85
|
-
flwr_path:
|
|
98
|
+
authentication_keys: (
|
|
99
|
+
tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey] | None
|
|
100
|
+
) = None,
|
|
101
|
+
max_retries: int | None = None,
|
|
102
|
+
max_wait_time: float | None = None,
|
|
103
|
+
flwr_path: Path | None = None,
|
|
86
104
|
isolation: str = ISOLATION_MODE_SUBPROCESS,
|
|
87
105
|
clientappio_api_address: str = CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
88
|
-
health_server_address:
|
|
106
|
+
health_server_address: str | None = None,
|
|
107
|
+
trusted_entities: dict[str, str] | None = None,
|
|
89
108
|
) -> None:
|
|
90
109
|
"""Start a Flower client node which connects to a Flower server.
|
|
91
110
|
|
|
@@ -137,14 +156,31 @@ def start_client_internal(
|
|
|
137
156
|
health_server_address : Optional[str] (default: None)
|
|
138
157
|
The address of the health server. If `None` is provided, the health server will
|
|
139
158
|
NOT be started.
|
|
159
|
+
trusted_entities : Optional[dict[str, str]] (default: None)
|
|
160
|
+
A dictionary mapping public key IDs to public keys.
|
|
161
|
+
Only apps verified by at least one of these
|
|
162
|
+
entities can run on a supernode.
|
|
140
163
|
"""
|
|
141
164
|
if insecure is None:
|
|
142
165
|
insecure = root_certificates is None
|
|
143
166
|
|
|
167
|
+
# Insecure HTTP is incompatible with authentication
|
|
168
|
+
if insecure and authentication_keys is not None:
|
|
169
|
+
url_v = f"https://flower.ai/docs/framework/v{package_version}/en/"
|
|
170
|
+
page = "how-to-authenticate-supernodes.html"
|
|
171
|
+
flwr_exit(
|
|
172
|
+
ExitCode.SUPERNODE_STARTED_WITHOUT_TLS_BUT_NODE_AUTH_ENABLED,
|
|
173
|
+
"Insecure connection is enabled, but the SuperNode's private key is "
|
|
174
|
+
"provided for authentication. SuperNode authentication requires a "
|
|
175
|
+
"secure TLS connection with the SuperLink. Please enable TLS by "
|
|
176
|
+
"providing the certificate via `--root-certificates`. Please refer "
|
|
177
|
+
f"to the Flower documentation for more information: {url_v}{page}",
|
|
178
|
+
)
|
|
179
|
+
|
|
144
180
|
# Initialize factories
|
|
145
|
-
state_factory = NodeStateFactory()
|
|
146
|
-
ffs_factory = FfsFactory(get_flwr_dir(flwr_path) / "supernode" / "ffs") # type: ignore
|
|
147
181
|
object_store_factory = ObjectStoreFactory()
|
|
182
|
+
state_factory = NodeStateFactory(objectstore_factory=object_store_factory)
|
|
183
|
+
ffs_factory = FfsFactory(get_flwr_dir(flwr_path) / "supernode" / "ffs") # type: ignore
|
|
148
184
|
|
|
149
185
|
# Launch ClientAppIo API server
|
|
150
186
|
grpc_servers = []
|
|
@@ -193,22 +229,18 @@ def start_client_internal(
|
|
|
193
229
|
max_wait_time=max_wait_time,
|
|
194
230
|
) as conn:
|
|
195
231
|
(
|
|
232
|
+
node_id,
|
|
196
233
|
receive,
|
|
197
234
|
send,
|
|
198
|
-
create_node,
|
|
199
|
-
_,
|
|
200
235
|
get_run,
|
|
201
236
|
get_fab,
|
|
202
237
|
pull_object,
|
|
203
238
|
push_object,
|
|
204
239
|
confirm_message_received,
|
|
205
240
|
) = conn
|
|
206
|
-
|
|
207
|
-
# Call create_node fn to register node
|
|
208
|
-
# and store node_id in state
|
|
209
|
-
if (node_id := create_node()) is None:
|
|
210
|
-
raise ValueError("Failed to register SuperNode with the SuperLink")
|
|
241
|
+
# Store node_id in state
|
|
211
242
|
state.set_node_id(node_id)
|
|
243
|
+
log(INFO, "SuperNode ID: %s", node_id)
|
|
212
244
|
|
|
213
245
|
# pylint: disable=too-many-nested-blocks
|
|
214
246
|
while True:
|
|
@@ -224,6 +256,7 @@ def start_client_internal(
|
|
|
224
256
|
get_fab=get_fab,
|
|
225
257
|
pull_object=pull_object,
|
|
226
258
|
confirm_message_received=confirm_message_received,
|
|
259
|
+
trusted_entities=trusted_entities,
|
|
227
260
|
)
|
|
228
261
|
|
|
229
262
|
# No message has been pulled therefore we can skip the push stage.
|
|
@@ -240,17 +273,34 @@ def start_client_internal(
|
|
|
240
273
|
)
|
|
241
274
|
|
|
242
275
|
|
|
276
|
+
def _insert_message(msg: Message, state: NodeState, store: ObjectStore) -> None:
|
|
277
|
+
"""Insert a message into the NodeState and ObjectStore."""
|
|
278
|
+
with no_object_id_recompute():
|
|
279
|
+
# Store message in state
|
|
280
|
+
msg.metadata.__dict__["_message_id"] = msg.object_id # Set message_id
|
|
281
|
+
state.store_message(msg)
|
|
282
|
+
|
|
283
|
+
# Preregister objects in ObjectStore
|
|
284
|
+
store.preregister(msg.metadata.run_id, get_object_tree(msg))
|
|
285
|
+
|
|
286
|
+
# Store all objects in ObjectStore
|
|
287
|
+
all_objects = get_all_nested_objects(msg)
|
|
288
|
+
for obj_id, obj in all_objects.items():
|
|
289
|
+
store.put(obj_id, obj.deflate())
|
|
290
|
+
|
|
291
|
+
|
|
243
292
|
def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
244
293
|
state: NodeState,
|
|
245
294
|
ffs: Ffs,
|
|
246
295
|
object_store: ObjectStore,
|
|
247
296
|
node_config: UserConfig,
|
|
248
|
-
receive: Callable[[],
|
|
297
|
+
receive: Callable[[], tuple[Message, ObjectTree] | None],
|
|
249
298
|
get_run: Callable[[int], Run],
|
|
250
299
|
get_fab: Callable[[str, int], Fab],
|
|
251
300
|
pull_object: Callable[[int, str], bytes],
|
|
252
301
|
confirm_message_received: Callable[[int, str], None],
|
|
253
|
-
|
|
302
|
+
trusted_entities: dict[str, str] | None,
|
|
303
|
+
) -> int | None:
|
|
254
304
|
"""Pull a message from the SuperLink and store it in the state.
|
|
255
305
|
|
|
256
306
|
This function current returns None if no message is received,
|
|
@@ -258,6 +308,7 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
|
258
308
|
This behavior will change in the future to return None after
|
|
259
309
|
completing transition to the `NodeState`-based SuperNode.
|
|
260
310
|
"""
|
|
311
|
+
# pylint: disable=too-many-nested-blocks
|
|
261
312
|
message = None
|
|
262
313
|
try:
|
|
263
314
|
# Pull message
|
|
@@ -278,7 +329,7 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
|
278
329
|
log(INFO, "[RUN %s]", message.metadata.run_id)
|
|
279
330
|
log(
|
|
280
331
|
INFO,
|
|
281
|
-
"
|
|
332
|
+
"Receiving: %s message (ID: %s)",
|
|
282
333
|
message.metadata.message_type,
|
|
283
334
|
message.metadata.message_id,
|
|
284
335
|
)
|
|
@@ -290,11 +341,32 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
|
290
341
|
if (run_info := state.get_run(run_id)) is None:
|
|
291
342
|
# Pull run info from SuperLink
|
|
292
343
|
run_info = get_run(run_id)
|
|
293
|
-
state.store_run(run_info)
|
|
294
344
|
|
|
295
345
|
# Pull and store the FAB
|
|
296
346
|
fab = get_fab(run_info.fab_hash, run_id)
|
|
297
|
-
|
|
347
|
+
|
|
348
|
+
# Verify the received FAB
|
|
349
|
+
# FAB must be signed if trust entities provided
|
|
350
|
+
if trusted_entities:
|
|
351
|
+
if not fab.verifications.get("valid_license", ""):
|
|
352
|
+
log(
|
|
353
|
+
WARN,
|
|
354
|
+
"App verification is not supported by the connected SuperLink.",
|
|
355
|
+
)
|
|
356
|
+
else:
|
|
357
|
+
fab_verified = _verify_fab(fab, trusted_entities)
|
|
358
|
+
if not fab_verified:
|
|
359
|
+
# Insert an error message in the state
|
|
360
|
+
# when FAB verification fails
|
|
361
|
+
log(
|
|
362
|
+
ERROR,
|
|
363
|
+
"FAB verification failed: the provided trusted entities "
|
|
364
|
+
"could not verify the FAB. An error reply "
|
|
365
|
+
"has been generated.",
|
|
366
|
+
)
|
|
367
|
+
reply = Message(FAB_VERIFICATION_ERROR, reply_to=message)
|
|
368
|
+
_insert_message(reply, state, object_store)
|
|
369
|
+
return run_id
|
|
298
370
|
|
|
299
371
|
# Initialize the context
|
|
300
372
|
run_cfg = get_fused_config_from_fab(fab.content, run_info)
|
|
@@ -305,7 +377,11 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
|
305
377
|
state=RecordDict(),
|
|
306
378
|
run_config=run_cfg,
|
|
307
379
|
)
|
|
380
|
+
|
|
381
|
+
# Store in the state
|
|
308
382
|
state.store_context(run_ctx)
|
|
383
|
+
state.store_run(run_info)
|
|
384
|
+
ffs.put(fab.content, fab.verifications)
|
|
309
385
|
|
|
310
386
|
# Preregister the object tree of the message
|
|
311
387
|
obj_ids_to_pull = object_store.preregister(run_id, object_tree)
|
|
@@ -313,16 +389,27 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
|
313
389
|
# Store the message in the state (note this message has no content)
|
|
314
390
|
state.store_message(message)
|
|
315
391
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
392
|
+
try:
|
|
393
|
+
# Pull and store objects of the message in the ObjectStore
|
|
394
|
+
obj_contents = pull_objects(
|
|
395
|
+
obj_ids_to_pull,
|
|
396
|
+
pull_object_fn=lambda obj_id: pull_object(run_id, obj_id),
|
|
397
|
+
)
|
|
398
|
+
for obj_id in list(obj_contents.keys()):
|
|
399
|
+
object_store.put(obj_id, obj_contents.pop(obj_id))
|
|
323
400
|
|
|
324
|
-
|
|
325
|
-
|
|
401
|
+
# Confirm that the message was received
|
|
402
|
+
confirm_message_received(run_id, message.metadata.message_id)
|
|
403
|
+
log(INFO, "Received successfully")
|
|
404
|
+
except Exception as err: # pylint: disable=broad-except
|
|
405
|
+
log(
|
|
406
|
+
ERROR,
|
|
407
|
+
"Failed to receive message %s: %s",
|
|
408
|
+
message.metadata.message_id,
|
|
409
|
+
err,
|
|
410
|
+
)
|
|
411
|
+
state.delete_messages(message_ids=[message.metadata.message_id])
|
|
412
|
+
object_store.delete(message.metadata.message_id)
|
|
326
413
|
|
|
327
414
|
except RunNotRunningException:
|
|
328
415
|
if message is None:
|
|
@@ -346,7 +433,7 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
|
346
433
|
def _push_messages(
|
|
347
434
|
state: NodeState,
|
|
348
435
|
object_store: ObjectStore,
|
|
349
|
-
send: Callable[[Message, ObjectTree], set[str]],
|
|
436
|
+
send: Callable[[Message, ObjectTree, float], set[str]],
|
|
350
437
|
push_object: Callable[[int, str, bytes], None],
|
|
351
438
|
) -> None:
|
|
352
439
|
"""Push reply messages to the SuperLink."""
|
|
@@ -369,8 +456,9 @@ def _push_messages(
|
|
|
369
456
|
log(INFO, "[RUN %s]", message.metadata.run_id)
|
|
370
457
|
log(
|
|
371
458
|
INFO,
|
|
372
|
-
"Sending: %s message",
|
|
459
|
+
"Sending: %s message (ID: %s)",
|
|
373
460
|
message.metadata.message_type,
|
|
461
|
+
message.metadata.message_id,
|
|
374
462
|
)
|
|
375
463
|
|
|
376
464
|
# Get the object tree for the message
|
|
@@ -393,9 +481,12 @@ def _push_messages(
|
|
|
393
481
|
|
|
394
482
|
# Send the message
|
|
395
483
|
try:
|
|
396
|
-
|
|
484
|
+
clientapp_runtime = state.get_message_processing_duration(
|
|
485
|
+
message_id=message.metadata.reply_to_message_id,
|
|
486
|
+
)
|
|
487
|
+
# Send the reply message with its ObjectTree and ClientApp runtime
|
|
397
488
|
# Get the IDs of objects to send
|
|
398
|
-
ids_obj_to_send = send(message, object_tree)
|
|
489
|
+
ids_obj_to_send = send(message, object_tree, clientapp_runtime)
|
|
399
490
|
|
|
400
491
|
# Push object contents from the ObjectStore
|
|
401
492
|
run_id = message.metadata.run_id
|
|
@@ -415,6 +506,13 @@ def _push_messages(
|
|
|
415
506
|
message.metadata.run_id,
|
|
416
507
|
message.metadata.message_id,
|
|
417
508
|
)
|
|
509
|
+
except Exception as err: # pylint: disable=broad-except
|
|
510
|
+
log(
|
|
511
|
+
ERROR,
|
|
512
|
+
"Failed to send message %s: %s",
|
|
513
|
+
message.metadata.message_id,
|
|
514
|
+
err,
|
|
515
|
+
)
|
|
418
516
|
finally:
|
|
419
517
|
# Delete the message from the state
|
|
420
518
|
state.delete_messages(
|
|
@@ -435,18 +533,17 @@ def _init_connection( # pylint: disable=too-many-positional-arguments
|
|
|
435
533
|
transport: str,
|
|
436
534
|
server_address: str,
|
|
437
535
|
insecure: bool,
|
|
438
|
-
root_certificates:
|
|
439
|
-
authentication_keys:
|
|
440
|
-
tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
|
|
441
|
-
|
|
442
|
-
max_retries:
|
|
443
|
-
max_wait_time:
|
|
536
|
+
root_certificates: bytes | str | None = None,
|
|
537
|
+
authentication_keys: (
|
|
538
|
+
tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey] | None
|
|
539
|
+
) = None,
|
|
540
|
+
max_retries: int | None = None,
|
|
541
|
+
max_wait_time: float | None = None,
|
|
444
542
|
) -> Iterator[
|
|
445
543
|
tuple[
|
|
446
|
-
|
|
447
|
-
Callable[[Message, ObjectTree]
|
|
448
|
-
Callable[[],
|
|
449
|
-
Callable[[], None],
|
|
544
|
+
int,
|
|
545
|
+
Callable[[], tuple[Message, ObjectTree] | None],
|
|
546
|
+
Callable[[Message, ObjectTree, float], set[str]],
|
|
450
547
|
Callable[[int], Run],
|
|
451
548
|
Callable[[str, int], Fab],
|
|
452
549
|
Callable[[int, str], bytes],
|
|
@@ -505,8 +602,8 @@ def _init_connection( # pylint: disable=too-many-positional-arguments
|
|
|
505
602
|
|
|
506
603
|
|
|
507
604
|
def _make_fleet_connection_retry_invoker(
|
|
508
|
-
max_retries:
|
|
509
|
-
max_wait_time:
|
|
605
|
+
max_retries: int | None = None,
|
|
606
|
+
max_wait_time: float | None = None,
|
|
510
607
|
connection_error_type: type[Exception] = RpcError,
|
|
511
608
|
) -> RetryInvoker:
|
|
512
609
|
"""Create a retry invoker for fleet connection."""
|
|
@@ -525,7 +622,7 @@ def run_clientappio_api_grpc(
|
|
|
525
622
|
state_factory: NodeStateFactory,
|
|
526
623
|
ffs_factory: FfsFactory,
|
|
527
624
|
objectstore_factory: ObjectStoreFactory,
|
|
528
|
-
certificates:
|
|
625
|
+
certificates: tuple[bytes, bytes, bytes] | None,
|
|
529
626
|
) -> grpc.Server:
|
|
530
627
|
"""Run ClientAppIo API gRPC server."""
|
|
531
628
|
clientappio_servicer: grpc.Server = ClientAppIoServicer(
|
|
@@ -546,3 +643,34 @@ def run_clientappio_api_grpc(
|
|
|
546
643
|
log(INFO, "Flower Deployment Runtime: Starting ClientAppIo API on %s", address)
|
|
547
644
|
clientappio_grpc_server.start()
|
|
548
645
|
return clientappio_grpc_server
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _verify_fab(fab: Fab, trusted_entities: dict[str, str]) -> bool:
|
|
649
|
+
"""Verify a FAB using its verification data and the provided trusted entities.
|
|
650
|
+
|
|
651
|
+
The FAB is considered verified if at least one trusted entity matches the
|
|
652
|
+
information contained in its verification records.
|
|
653
|
+
"""
|
|
654
|
+
verifications = fab.verifications
|
|
655
|
+
verif_full = {
|
|
656
|
+
k: json.loads(v) for k, v in verifications.items() if k != "valid_license"
|
|
657
|
+
}
|
|
658
|
+
fab_verified = False
|
|
659
|
+
for public_key_id, verif in verif_full.items():
|
|
660
|
+
if public_key_id in trusted_entities:
|
|
661
|
+
verifier_public_key = load_ssh_public_key(
|
|
662
|
+
trusted_entities[public_key_id].encode("utf-8")
|
|
663
|
+
)
|
|
664
|
+
message_to_verify = create_message_to_sign(
|
|
665
|
+
hashlib.sha256(fab.content).digest(),
|
|
666
|
+
verif["signed_at"],
|
|
667
|
+
)
|
|
668
|
+
assert isinstance(verifier_public_key, ed25519.Ed25519PublicKey)
|
|
669
|
+
if verify_signature(
|
|
670
|
+
verifier_public_key,
|
|
671
|
+
message_to_verify,
|
|
672
|
+
decode_base64url(verif["signature"]),
|
|
673
|
+
):
|
|
674
|
+
fab_verified = True
|
|
675
|
+
break
|
|
676
|
+
return fab_verified
|
{flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/METADATA
RENAMED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: flwr-nightly
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.26.0.dev20260121
|
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
|
|
7
7
|
Author: The Flower Authors
|
|
8
8
|
Author-email: hello@flower.ai
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -20,7 +20,6 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
22
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
24
23
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
25
24
|
Classifier: Topic :: Scientific/Engineering
|
|
26
25
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
@@ -31,23 +30,25 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
31
30
|
Classifier: Typing :: Typed
|
|
32
31
|
Provides-Extra: rest
|
|
33
32
|
Provides-Extra: simulation
|
|
33
|
+
Requires-Dist: SQLAlchemy (>=2.0.45,<3.0.0)
|
|
34
34
|
Requires-Dist: click (<8.2.0)
|
|
35
35
|
Requires-Dist: cryptography (>=44.0.1,<45.0.0)
|
|
36
|
-
Requires-Dist: grpcio (>=1.
|
|
37
|
-
Requires-Dist: grpcio-health-checking (>=1.
|
|
36
|
+
Requires-Dist: grpcio (>=1.70.0,<2.0.0)
|
|
37
|
+
Requires-Dist: grpcio-health-checking (>=1.70.0,<2.0.0)
|
|
38
38
|
Requires-Dist: iterators (>=0.0.2,<0.0.3)
|
|
39
39
|
Requires-Dist: numpy (>=1.26.0,<3.0.0)
|
|
40
40
|
Requires-Dist: pathspec (>=0.12.1,<0.13.0)
|
|
41
|
-
Requires-Dist: protobuf (>=
|
|
41
|
+
Requires-Dist: protobuf (>=5.28.0,<7.0.0)
|
|
42
42
|
Requires-Dist: pycryptodome (>=3.18.0,<4.0.0)
|
|
43
43
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
44
|
-
Requires-Dist: ray (==2.
|
|
44
|
+
Requires-Dist: ray (==2.51.1) ; (python_version >= "3.10" and python_version < "3.13") and (extra == "simulation")
|
|
45
|
+
Requires-Dist: ray (==2.51.1) ; (sys_platform != "win32" and python_version == "3.13") and (extra == "simulation")
|
|
45
46
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
46
47
|
Requires-Dist: rich (>=13.5.0,<14.0.0)
|
|
47
48
|
Requires-Dist: starlette (>=0.45.2,<0.46.0) ; extra == "rest"
|
|
48
49
|
Requires-Dist: tomli (>=2.0.1,<3.0.0)
|
|
49
50
|
Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
|
|
50
|
-
Requires-Dist: typer (>=0.12.5,<0.
|
|
51
|
+
Requires-Dist: typer (>=0.12.5,<0.21.0)
|
|
51
52
|
Requires-Dist: uvicorn[standard] (>=0.34.0,<0.35.0) ; extra == "rest"
|
|
52
53
|
Project-URL: Documentation, https://flower.ai
|
|
53
54
|
Project-URL: Homepage, https://flower.ai
|
|
@@ -180,7 +181,7 @@ Quickstart examples:
|
|
|
180
181
|
- [Quickstart (Pandas)](https://github.com/adap/flower/tree/main/examples/quickstart-pandas)
|
|
181
182
|
- [Quickstart (JAX)](https://github.com/adap/flower/tree/main/examples/quickstart-jax)
|
|
182
183
|
- [Quickstart (MONAI)](https://github.com/adap/flower/tree/main/examples/quickstart-monai)
|
|
183
|
-
- [Quickstart (scikit-learn)](https://github.com/adap/flower/tree/main/examples/sklearn
|
|
184
|
+
- [Quickstart (scikit-learn)](https://github.com/adap/flower/tree/main/examples/quickstart-sklearn)
|
|
184
185
|
- [Quickstart (Android [TFLite])](https://github.com/adap/flower/tree/main/examples/android)
|
|
185
186
|
- [Quickstart (iOS [CoreML])](https://github.com/adap/flower/tree/main/examples/ios)
|
|
186
187
|
- [Quickstart (MLX)](https://github.com/adap/flower/tree/main/examples/quickstart-mlx)
|
|
@@ -197,10 +198,8 @@ Other [examples](https://github.com/adap/flower/tree/main/examples):
|
|
|
197
198
|
- [Advanced Flower with TensorFlow/Keras](https://github.com/adap/flower/tree/main/examples/advanced-tensorflow)
|
|
198
199
|
- [Advanced Flower with PyTorch](https://github.com/adap/flower/tree/main/examples/advanced-pytorch)
|
|
199
200
|
- [Comprehensive Flower+XGBoost](https://github.com/adap/flower/tree/main/examples/xgboost-comprehensive)
|
|
200
|
-
- [Flower through Docker Compose and with Grafana dashboard](https://github.com/adap/flower/tree/main/examples/flower-via-docker-compose)
|
|
201
201
|
- [Flower with KaplanMeierFitter from the lifelines library](https://github.com/adap/flower/tree/main/examples/federated-kaplan-meier-fitter)
|
|
202
202
|
- [Sample Level Privacy with Opacus](https://github.com/adap/flower/tree/main/examples/opacus)
|
|
203
|
-
- [Sample Level Privacy with TensorFlow-Privacy](https://github.com/adap/flower/tree/main/examples/tensorflow-privacy)
|
|
204
203
|
- [Flower with a Tabular Dataset](https://github.com/adap/flower/tree/main/examples/fl-tabular)
|
|
205
204
|
|
|
206
205
|
## Community
|