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
|
@@ -17,19 +17,14 @@
|
|
|
17
17
|
|
|
18
18
|
import traceback
|
|
19
19
|
from logging import ERROR
|
|
20
|
-
from typing import Optional
|
|
21
20
|
|
|
22
21
|
from flwr import common
|
|
22
|
+
from flwr.app.message_type import MessageType
|
|
23
23
|
from flwr.client import ClientFnExt
|
|
24
|
-
from flwr.client.client_app import ClientApp
|
|
25
24
|
from flwr.client.run_info_store import DeprecatedRunInfoStore
|
|
25
|
+
from flwr.clientapp.client_app import ClientApp
|
|
26
26
|
from flwr.common import DEFAULT_TTL, Message, Metadata, RecordDict, now
|
|
27
|
-
from flwr.common.constant import
|
|
28
|
-
NUM_PARTITIONS_KEY,
|
|
29
|
-
PARTITION_ID_KEY,
|
|
30
|
-
MessageType,
|
|
31
|
-
MessageTypeLegacy,
|
|
32
|
-
)
|
|
27
|
+
from flwr.common.constant import NUM_PARTITIONS_KEY, PARTITION_ID_KEY, MessageTypeLegacy
|
|
33
28
|
from flwr.common.logger import log
|
|
34
29
|
from flwr.common.message import make_message
|
|
35
30
|
from flwr.common.recorddict_compat import (
|
|
@@ -74,7 +69,7 @@ class RayActorClientProxy(ClientProxy):
|
|
|
74
69
|
},
|
|
75
70
|
)
|
|
76
71
|
|
|
77
|
-
def _submit_job(self, message: Message, timeout:
|
|
72
|
+
def _submit_job(self, message: Message, timeout: float | None) -> Message:
|
|
78
73
|
"""Sumbit a message to the ActorPool."""
|
|
79
74
|
run_id = message.metadata.run_id
|
|
80
75
|
|
|
@@ -114,8 +109,8 @@ class RayActorClientProxy(ClientProxy):
|
|
|
114
109
|
self,
|
|
115
110
|
recorddict: RecordDict,
|
|
116
111
|
message_type: str,
|
|
117
|
-
timeout:
|
|
118
|
-
group_id:
|
|
112
|
+
timeout: float | None,
|
|
113
|
+
group_id: int | None,
|
|
119
114
|
) -> Message:
|
|
120
115
|
"""Wrap a RecordDict inside a Message."""
|
|
121
116
|
return make_message(
|
|
@@ -136,8 +131,8 @@ class RayActorClientProxy(ClientProxy):
|
|
|
136
131
|
def get_properties(
|
|
137
132
|
self,
|
|
138
133
|
ins: common.GetPropertiesIns,
|
|
139
|
-
timeout:
|
|
140
|
-
group_id:
|
|
134
|
+
timeout: float | None,
|
|
135
|
+
group_id: int | None,
|
|
141
136
|
) -> common.GetPropertiesRes:
|
|
142
137
|
"""Return client's properties."""
|
|
143
138
|
recorddict = getpropertiesins_to_recorddict(ins)
|
|
@@ -155,8 +150,8 @@ class RayActorClientProxy(ClientProxy):
|
|
|
155
150
|
def get_parameters(
|
|
156
151
|
self,
|
|
157
152
|
ins: common.GetParametersIns,
|
|
158
|
-
timeout:
|
|
159
|
-
group_id:
|
|
153
|
+
timeout: float | None,
|
|
154
|
+
group_id: int | None,
|
|
160
155
|
) -> common.GetParametersRes:
|
|
161
156
|
"""Return the current local model parameters."""
|
|
162
157
|
recorddict = getparametersins_to_recorddict(ins)
|
|
@@ -172,7 +167,7 @@ class RayActorClientProxy(ClientProxy):
|
|
|
172
167
|
return recorddict_to_getparametersres(message_out.content, keep_input=False)
|
|
173
168
|
|
|
174
169
|
def fit(
|
|
175
|
-
self, ins: common.FitIns, timeout:
|
|
170
|
+
self, ins: common.FitIns, timeout: float | None, group_id: int | None
|
|
176
171
|
) -> common.FitRes:
|
|
177
172
|
"""Train model parameters on the locally held dataset."""
|
|
178
173
|
recorddict = fitins_to_recorddict(
|
|
@@ -190,7 +185,7 @@ class RayActorClientProxy(ClientProxy):
|
|
|
190
185
|
return recorddict_to_fitres(message_out.content, keep_input=False)
|
|
191
186
|
|
|
192
187
|
def evaluate(
|
|
193
|
-
self, ins: common.EvaluateIns, timeout:
|
|
188
|
+
self, ins: common.EvaluateIns, timeout: float | None, group_id: int | None
|
|
194
189
|
) -> common.EvaluateRes:
|
|
195
190
|
"""Evaluate model parameters on the locally held dataset."""
|
|
196
191
|
recorddict = evaluateins_to_recorddict(
|
|
@@ -210,8 +205,8 @@ class RayActorClientProxy(ClientProxy):
|
|
|
210
205
|
def reconnect(
|
|
211
206
|
self,
|
|
212
207
|
ins: common.ReconnectIns,
|
|
213
|
-
timeout:
|
|
214
|
-
group_id:
|
|
208
|
+
timeout: float | None,
|
|
209
|
+
group_id: int | None,
|
|
215
210
|
) -> common.DisconnectRes:
|
|
216
211
|
"""Disconnect and (optionally) reconnect later."""
|
|
217
212
|
return common.DisconnectRes(reason="") # Nothing to do here (yet)
|
|
@@ -26,11 +26,12 @@ import traceback
|
|
|
26
26
|
from logging import DEBUG, ERROR, INFO, WARNING
|
|
27
27
|
from pathlib import Path
|
|
28
28
|
from queue import Empty, Queue
|
|
29
|
-
from typing import Any,
|
|
29
|
+
from typing import Any, cast
|
|
30
30
|
|
|
31
|
+
from flwr.app.user_config import UserConfig
|
|
31
32
|
from flwr.cli.config_utils import load_and_validate
|
|
32
33
|
from flwr.cli.utils import get_sha256_hash
|
|
33
|
-
from flwr.
|
|
34
|
+
from flwr.clientapp import ClientApp
|
|
34
35
|
from flwr.common import Context, EventType, RecordDict, event, log, now
|
|
35
36
|
from flwr.common.config import get_fused_config_from_dir, parse_config_args
|
|
36
37
|
from flwr.common.constant import RUN_ID_NUM_BYTES, Status
|
|
@@ -39,7 +40,7 @@ from flwr.common.logger import (
|
|
|
39
40
|
update_console_handler,
|
|
40
41
|
warn_deprecated_feature_with_example,
|
|
41
42
|
)
|
|
42
|
-
from flwr.common.typing import Run, RunStatus
|
|
43
|
+
from flwr.common.typing import Run, RunStatus
|
|
43
44
|
from flwr.server.grid import Grid, InMemoryGrid
|
|
44
45
|
from flwr.server.run_serverapp import run as _run
|
|
45
46
|
from flwr.server.server_app import ServerApp
|
|
@@ -51,6 +52,9 @@ from flwr.server.superlink.linkstate.utils import generate_rand_int_from_bytes
|
|
|
51
52
|
from flwr.simulation.ray_transport.utils import (
|
|
52
53
|
enable_tf_gpu_growth as enable_gpu_growth,
|
|
53
54
|
)
|
|
55
|
+
from flwr.supercore.constant import FLWR_IN_MEMORY_DB_NAME, NOOP_FEDERATION
|
|
56
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
|
57
|
+
from flwr.superlink.federation import NoOpFederationManager
|
|
54
58
|
|
|
55
59
|
|
|
56
60
|
def _replace_keys(d: Any, match: str, target: str) -> Any:
|
|
@@ -98,12 +102,7 @@ def run_simulation_from_cli() -> None:
|
|
|
98
102
|
_check_ray_support(args.backend)
|
|
99
103
|
|
|
100
104
|
# Load JSON config
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if backend_config_dict:
|
|
104
|
-
# Backend config internally operates with `_` not with `-`
|
|
105
|
-
backend_config_dict = _replace_keys(backend_config_dict, match="-", target="_")
|
|
106
|
-
log(DEBUG, "backend_config_dict: %s", backend_config_dict)
|
|
105
|
+
backend_config = json.loads(args.backend_config)
|
|
107
106
|
|
|
108
107
|
run_id = (
|
|
109
108
|
generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
|
@@ -141,6 +140,7 @@ def run_simulation_from_cli() -> None:
|
|
|
141
140
|
|
|
142
141
|
# Create run
|
|
143
142
|
run = Run.create_empty(run_id)
|
|
143
|
+
run.federation = NOOP_FEDERATION
|
|
144
144
|
run.override_config = override_config
|
|
145
145
|
|
|
146
146
|
# Create Context
|
|
@@ -157,7 +157,7 @@ def run_simulation_from_cli() -> None:
|
|
|
157
157
|
client_app_attr=client_app_attr,
|
|
158
158
|
num_supernodes=args.num_supernodes,
|
|
159
159
|
backend_name=args.backend,
|
|
160
|
-
backend_config=
|
|
160
|
+
backend_config=backend_config,
|
|
161
161
|
app_dir=args.app,
|
|
162
162
|
run=run,
|
|
163
163
|
enable_tf_gpu_growth=args.enable_tf_gpu_growth,
|
|
@@ -175,7 +175,7 @@ def run_simulation(
|
|
|
175
175
|
client_app: ClientApp,
|
|
176
176
|
num_supernodes: int,
|
|
177
177
|
backend_name: str = "ray",
|
|
178
|
-
backend_config:
|
|
178
|
+
backend_config: BackendConfig | None = None,
|
|
179
179
|
enable_tf_gpu_growth: bool = False,
|
|
180
180
|
verbose_logging: bool = False,
|
|
181
181
|
) -> None:
|
|
@@ -248,8 +248,8 @@ def run_simulation(
|
|
|
248
248
|
|
|
249
249
|
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
250
250
|
def run_serverapp_th(
|
|
251
|
-
server_app_attr:
|
|
252
|
-
server_app:
|
|
251
|
+
server_app_attr: str | None,
|
|
252
|
+
server_app: ServerApp | None,
|
|
253
253
|
server_app_context: Context,
|
|
254
254
|
grid: Grid,
|
|
255
255
|
app_dir: str,
|
|
@@ -266,8 +266,8 @@ def run_serverapp_th(
|
|
|
266
266
|
exception_event: threading.Event,
|
|
267
267
|
_grid: Grid,
|
|
268
268
|
_server_app_dir: str,
|
|
269
|
-
_server_app_attr:
|
|
270
|
-
_server_app:
|
|
269
|
+
_server_app_attr: str | None,
|
|
270
|
+
_server_app: ServerApp | None,
|
|
271
271
|
_ctx_queue: "Queue[Context]",
|
|
272
272
|
) -> None:
|
|
273
273
|
"""Run SeverApp, after check if GPU memory growth has to be set.
|
|
@@ -327,16 +327,18 @@ def _main_loop(
|
|
|
327
327
|
enable_tf_gpu_growth: bool,
|
|
328
328
|
run: Run,
|
|
329
329
|
exit_event: EventType,
|
|
330
|
-
flwr_dir:
|
|
331
|
-
client_app:
|
|
332
|
-
client_app_attr:
|
|
333
|
-
server_app:
|
|
334
|
-
server_app_attr:
|
|
335
|
-
server_app_context:
|
|
330
|
+
flwr_dir: str | None = None,
|
|
331
|
+
client_app: ClientApp | None = None,
|
|
332
|
+
client_app_attr: str | None = None,
|
|
333
|
+
server_app: ServerApp | None = None,
|
|
334
|
+
server_app_attr: str | None = None,
|
|
335
|
+
server_app_context: Context | None = None,
|
|
336
336
|
) -> Context:
|
|
337
337
|
"""Start ServerApp on a separate thread, then launch Simulation Engine."""
|
|
338
338
|
# Initialize StateFactory
|
|
339
|
-
state_factory = LinkStateFactory(
|
|
339
|
+
state_factory = LinkStateFactory(
|
|
340
|
+
FLWR_IN_MEMORY_DB_NAME, NoOpFederationManager(), ObjectStoreFactory()
|
|
341
|
+
)
|
|
340
342
|
|
|
341
343
|
f_stop = threading.Event()
|
|
342
344
|
# A Threading event to indicate if an exception was raised in the ServerApp thread
|
|
@@ -427,16 +429,16 @@ def _main_loop(
|
|
|
427
429
|
def _run_simulation(
|
|
428
430
|
num_supernodes: int,
|
|
429
431
|
exit_event: EventType,
|
|
430
|
-
client_app:
|
|
431
|
-
server_app:
|
|
432
|
+
client_app: ClientApp | None = None,
|
|
433
|
+
server_app: ServerApp | None = None,
|
|
432
434
|
backend_name: str = "ray",
|
|
433
|
-
backend_config:
|
|
434
|
-
client_app_attr:
|
|
435
|
-
server_app_attr:
|
|
436
|
-
server_app_context:
|
|
435
|
+
backend_config: BackendConfig | None = None,
|
|
436
|
+
client_app_attr: str | None = None,
|
|
437
|
+
server_app_attr: str | None = None,
|
|
438
|
+
server_app_context: Context | None = None,
|
|
437
439
|
app_dir: str = "",
|
|
438
|
-
flwr_dir:
|
|
439
|
-
run:
|
|
440
|
+
flwr_dir: str | None = None,
|
|
441
|
+
run: Run | None = None,
|
|
440
442
|
enable_tf_gpu_growth: bool = False,
|
|
441
443
|
verbose_logging: bool = False,
|
|
442
444
|
is_app: bool = False,
|
|
@@ -444,29 +446,28 @@ def _run_simulation(
|
|
|
444
446
|
"""Launch the Simulation Engine."""
|
|
445
447
|
if backend_config is None:
|
|
446
448
|
backend_config = {}
|
|
449
|
+
elif backend_config:
|
|
450
|
+
# Backend config internally operates with `_` not with `-`
|
|
451
|
+
backend_config = cast(
|
|
452
|
+
BackendConfig, _replace_keys(backend_config, match="-", target="_")
|
|
453
|
+
)
|
|
454
|
+
log(DEBUG, "backend_config: %s", backend_config)
|
|
447
455
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
456
|
+
# Set default init_args if not passed
|
|
457
|
+
backend_config.setdefault("init_args", {})
|
|
451
458
|
# Set default client_resources if not passed
|
|
452
|
-
|
|
453
|
-
backend_config["client_resources"] = {"num_cpus": 2, "num_gpus": 0}
|
|
454
|
-
|
|
459
|
+
backend_config.setdefault("client_resources", {"num_cpus": 2, "num_gpus": 0})
|
|
455
460
|
# Initialization of backend config to enable GPU growth globally when set
|
|
456
|
-
|
|
457
|
-
backend_config["actor"] = {"tensorflow": 0}
|
|
461
|
+
backend_config.setdefault("actor", {"tensorflow": 0})
|
|
458
462
|
|
|
459
463
|
# Set logging level
|
|
460
464
|
logger = logging.getLogger("flwr")
|
|
461
465
|
if verbose_logging:
|
|
462
466
|
update_console_handler(level=DEBUG, timestamps=True, colored=True)
|
|
463
467
|
else:
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
)
|
|
467
|
-
backend_config["init_args"]["log_to_driver"] = backend_config["init_args"].get(
|
|
468
|
-
"log_to_driver", True
|
|
469
|
-
)
|
|
468
|
+
init_args = backend_config["init_args"]
|
|
469
|
+
init_args.setdefault("logging_level", WARNING)
|
|
470
|
+
init_args.setdefault("log_to_driver", True)
|
|
470
471
|
|
|
471
472
|
if enable_tf_gpu_growth:
|
|
472
473
|
# Check that Backend config has also enabled using GPU growth
|
|
@@ -482,6 +483,7 @@ def _run_simulation(
|
|
|
482
483
|
if run is None:
|
|
483
484
|
run_id = generate_rand_int_from_bytes(RUN_ID_NUM_BYTES)
|
|
484
485
|
run = Run.create_empty(run_id=run_id)
|
|
486
|
+
run.federation = NOOP_FEDERATION
|
|
485
487
|
|
|
486
488
|
args = (
|
|
487
489
|
num_supernodes,
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from logging import DEBUG, WARNING
|
|
19
|
-
from typing import
|
|
19
|
+
from typing import cast
|
|
20
20
|
|
|
21
21
|
import grpc
|
|
22
22
|
|
|
@@ -43,12 +43,12 @@ class SimulationIoConnection:
|
|
|
43
43
|
def __init__( # pylint: disable=too-many-arguments
|
|
44
44
|
self,
|
|
45
45
|
simulationio_service_address: str = SIMULATIONIO_API_DEFAULT_CLIENT_ADDRESS,
|
|
46
|
-
root_certificates:
|
|
46
|
+
root_certificates: bytes | None = None,
|
|
47
47
|
) -> None:
|
|
48
48
|
self._addr = simulationio_service_address
|
|
49
49
|
self._cert = root_certificates
|
|
50
|
-
self._grpc_stub:
|
|
51
|
-
self._channel:
|
|
50
|
+
self._grpc_stub: SimulationIoStub | None = None
|
|
51
|
+
self._channel: grpc.Channel | None = None
|
|
52
52
|
self._retry_invoker = _make_simple_grpc_retry_invoker()
|
|
53
53
|
|
|
54
54
|
@property
|
|
@@ -15,17 +15,13 @@
|
|
|
15
15
|
"""Flower IP address utils."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
import re
|
|
19
18
|
import socket
|
|
20
19
|
from ipaddress import ip_address
|
|
21
|
-
from typing import Optional
|
|
22
|
-
|
|
23
|
-
import grpc
|
|
24
20
|
|
|
25
21
|
IPV6: int = 6
|
|
26
22
|
|
|
27
23
|
|
|
28
|
-
def parse_address(address: str) ->
|
|
24
|
+
def parse_address(address: str) -> tuple[str, int, bool | None] | None:
|
|
29
25
|
"""Parse an IP address into host, port, and version.
|
|
30
26
|
|
|
31
27
|
Parameters
|
|
@@ -104,35 +100,3 @@ def is_port_in_use(address: str) -> bool:
|
|
|
104
100
|
return True
|
|
105
101
|
|
|
106
102
|
return False
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def get_ip_address_from_servicer_context(context: grpc.ServicerContext) -> str:
|
|
110
|
-
"""Extract the client's IPv4 or IPv6 address from the gRPC ServicerContext.
|
|
111
|
-
|
|
112
|
-
Parameters
|
|
113
|
-
----------
|
|
114
|
-
context : grpc.ServicerContext
|
|
115
|
-
The gRPC ServicerContext object. The context.peer() returns a string like
|
|
116
|
-
"ipv4:127.0.0.1:56789" for IPv4 and "ipv6:[2001:db8::1]:54321" for IPv6.
|
|
117
|
-
|
|
118
|
-
Returns
|
|
119
|
-
-------
|
|
120
|
-
str
|
|
121
|
-
If one of the format matches, the function will return the client's IP address,
|
|
122
|
-
otherwise, it will raise a ValueError.
|
|
123
|
-
"""
|
|
124
|
-
peer: str = context.peer()
|
|
125
|
-
# Match IPv4: "ipv4:IP:port"
|
|
126
|
-
ipv4_match = re.match(r"^ipv4:(?P<ip>[^:]+):", peer)
|
|
127
|
-
if ipv4_match:
|
|
128
|
-
return ipv4_match.group("ip")
|
|
129
|
-
|
|
130
|
-
# Match IPv6: "ipv6:[IP]:port"
|
|
131
|
-
ipv6_match = re.match(r"^ipv6:\[(?P<ip>[^\]]+)\]:", peer)
|
|
132
|
-
if ipv6_match:
|
|
133
|
-
return ipv6_match.group("ip")
|
|
134
|
-
|
|
135
|
-
raise ValueError(
|
|
136
|
-
f"Unsupported peer address format: {peer} for the transport protocol. "
|
|
137
|
-
"The supported formats are ipv4:IP:port and ipv6:[IP]:port."
|
|
138
|
-
)
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
19
19
|
from logging import INFO
|
|
20
|
-
from typing import Any
|
|
20
|
+
from typing import Any
|
|
21
21
|
|
|
22
22
|
import yaml
|
|
23
23
|
|
|
@@ -54,7 +54,7 @@ except ImportError:
|
|
|
54
54
|
|
|
55
55
|
def get_ee_plugin_and_stub_class( # pylint: disable=unused-argument
|
|
56
56
|
plugin_type: str,
|
|
57
|
-
) ->
|
|
57
|
+
) -> tuple[type[ExecPlugin], type[object]] | None:
|
|
58
58
|
"""Get the EE plugin class and stub class based on the plugin type."""
|
|
59
59
|
return None
|
|
60
60
|
|
|
@@ -75,7 +75,6 @@ def flower_superexec() -> None:
|
|
|
75
75
|
# Log the first message after parsing arguments in case of `--help`
|
|
76
76
|
log(INFO, "Starting Flower SuperExec")
|
|
77
77
|
|
|
78
|
-
# Trigger telemetry event
|
|
79
78
|
event(EventType.RUN_SUPEREXEC_ENTER, {"plugin_type": args.plugin_type})
|
|
80
79
|
|
|
81
80
|
# Load plugin config from YAML file if provided
|
|
@@ -83,7 +82,7 @@ def flower_superexec() -> None:
|
|
|
83
82
|
if plugin_config_path := getattr(args, "plugin_config", None):
|
|
84
83
|
try:
|
|
85
84
|
with open(plugin_config_path, encoding="utf-8") as file:
|
|
86
|
-
yaml_config:
|
|
85
|
+
yaml_config: dict[str, Any] | None = yaml.safe_load(file)
|
|
87
86
|
if yaml_config is None or EXEC_PLUGIN_SECTION not in yaml_config:
|
|
88
87
|
raise ValueError(f"Missing '{EXEC_PLUGIN_SECTION}' section.")
|
|
89
88
|
plugin_config = yaml_config[EXEC_PLUGIN_SECTION]
|
flwr/supercore/constant.py
CHANGED
|
@@ -15,5 +15,74 @@
|
|
|
15
15
|
"""Constants for Flower infrastructure."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from flwr.common.constant import FLWR_DIR
|
|
21
|
+
|
|
18
22
|
# Top-level key in YAML config for exec plugin settings
|
|
19
23
|
EXEC_PLUGIN_SECTION = "exec_plugin"
|
|
24
|
+
|
|
25
|
+
# Flower in-memory Python-based database name
|
|
26
|
+
FLWR_IN_MEMORY_DB_NAME = ":flwr-in-memory:"
|
|
27
|
+
|
|
28
|
+
# Constants for Hub
|
|
29
|
+
APP_ID_PATTERN = r"^@[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
|
|
30
|
+
APP_VERSION_PATTERN = r"^\d+\.\d+\.\d+$"
|
|
31
|
+
PLATFORM_API_URL = "https://api.flower.ai/v1"
|
|
32
|
+
|
|
33
|
+
# Specification for app publishing
|
|
34
|
+
APP_PUBLISH_INCLUDE_PATTERNS = (
|
|
35
|
+
"**/*.py",
|
|
36
|
+
"**/*.toml",
|
|
37
|
+
"**/*.md",
|
|
38
|
+
)
|
|
39
|
+
APP_PUBLISH_EXCLUDE_PATTERNS = FAB_EXCLUDE_PATTERNS = (
|
|
40
|
+
f"{FLWR_DIR}/**", # Exclude the .flwr directory
|
|
41
|
+
"**/__pycache__/**",
|
|
42
|
+
)
|
|
43
|
+
MAX_TOTAL_BYTES = 10 * 1024 * 1024 # 10 MB
|
|
44
|
+
MAX_FILE_BYTES = 1 * 1024 * 1024 # 1 MB
|
|
45
|
+
MAX_FILE_COUNT = 1000
|
|
46
|
+
MAX_DIR_DEPTH = 10 # relative depth (number of parts in relpath)
|
|
47
|
+
UTF8 = "utf-8"
|
|
48
|
+
MIME_MAP = {
|
|
49
|
+
".py": "text/x-python; charset=utf-8",
|
|
50
|
+
".md": "text/markdown; charset=utf-8",
|
|
51
|
+
".toml": "application/toml; charset=utf-8",
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Constants for federations
|
|
55
|
+
NOOP_FEDERATION = "default"
|
|
56
|
+
|
|
57
|
+
# Constants for exit handling
|
|
58
|
+
FORCE_EXIT_TIMEOUT_SECONDS = 5 # Used in `flwr_exit` function
|
|
59
|
+
|
|
60
|
+
# Constants for message processing timing
|
|
61
|
+
MESSAGE_TIME_ENTRY_MAX_AGE_SECONDS = 3600
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# System message type
|
|
65
|
+
SYSTEM_MESSAGE_TYPE = "system"
|
|
66
|
+
|
|
67
|
+
# SQLite PRAGMA settings for optimal performance and correctness
|
|
68
|
+
SQLITE_PRAGMAS = (
|
|
69
|
+
("journal_mode", "WAL"), # Enable Write-Ahead Logging for better concurrency
|
|
70
|
+
("synchronous", "NORMAL"),
|
|
71
|
+
("foreign_keys", "ON"),
|
|
72
|
+
("cache_size", "-64000"), # 64MB cache
|
|
73
|
+
("temp_store", "MEMORY"), # In-memory temp tables
|
|
74
|
+
("mmap_size", "268435456"), # 256MB memory-mapped I/O
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class NodeStatus:
|
|
79
|
+
"""Event log writer types."""
|
|
80
|
+
|
|
81
|
+
REGISTERED = "registered"
|
|
82
|
+
ONLINE = "online"
|
|
83
|
+
OFFLINE = "offline"
|
|
84
|
+
UNREGISTERED = "unregistered"
|
|
85
|
+
|
|
86
|
+
def __new__(cls) -> NodeStatus:
|
|
87
|
+
"""Prevent instantiation."""
|
|
88
|
+
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
|
@@ -16,14 +16,20 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from abc import ABC, abstractmethod
|
|
19
|
-
|
|
19
|
+
|
|
20
|
+
from ..object_store import ObjectStore
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class CoreState(ABC):
|
|
23
24
|
"""Abstract base class for core state."""
|
|
24
25
|
|
|
26
|
+
@property
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def object_store(self) -> ObjectStore:
|
|
29
|
+
"""Return the ObjectStore instance used by this CoreState."""
|
|
30
|
+
|
|
25
31
|
@abstractmethod
|
|
26
|
-
def create_token(self, run_id: int) ->
|
|
32
|
+
def create_token(self, run_id: int) -> str | None:
|
|
27
33
|
"""Create a token for the given run ID.
|
|
28
34
|
|
|
29
35
|
Parameters
|
|
@@ -66,7 +72,7 @@ class CoreState(ABC):
|
|
|
66
72
|
"""
|
|
67
73
|
|
|
68
74
|
@abstractmethod
|
|
69
|
-
def get_run_id_by_token(self, token: str) ->
|
|
75
|
+
def get_run_id_by_token(self, token: str) -> int | None:
|
|
70
76
|
"""Get the run ID associated with a given token.
|
|
71
77
|
|
|
72
78
|
Parameters
|
|
@@ -79,3 +85,18 @@ class CoreState(ABC):
|
|
|
79
85
|
Optional[int]
|
|
80
86
|
The run ID if the token is valid, otherwise None.
|
|
81
87
|
"""
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
def acknowledge_app_heartbeat(self, token: str) -> bool:
|
|
91
|
+
"""Acknowledge an app heartbeat with the provided token.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
token : str
|
|
96
|
+
The token associated with the app.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
bool
|
|
101
|
+
True if the heartbeat is acknowledged successfully, False otherwise.
|
|
102
|
+
"""
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Copyright 2025 Flower Labs GmbH. All Rights Reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
# ==============================================================================
|
|
15
|
+
"""In-memory CoreState implementation."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import secrets
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from threading import Lock
|
|
21
|
+
|
|
22
|
+
from flwr.common import now
|
|
23
|
+
from flwr.common.constant import (
|
|
24
|
+
FLWR_APP_TOKEN_LENGTH,
|
|
25
|
+
HEARTBEAT_DEFAULT_INTERVAL,
|
|
26
|
+
HEARTBEAT_PATIENCE,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from ..object_store import ObjectStore
|
|
30
|
+
from .corestate import CoreState
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class TokenRecord:
|
|
35
|
+
"""Record containing token and heartbeat information."""
|
|
36
|
+
|
|
37
|
+
token: str
|
|
38
|
+
active_until: float
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class InMemoryCoreState(CoreState):
|
|
42
|
+
"""In-memory CoreState implementation."""
|
|
43
|
+
|
|
44
|
+
def __init__(self, object_store: ObjectStore) -> None:
|
|
45
|
+
self._object_store = object_store
|
|
46
|
+
# Store run ID to token mapping and token to run ID mapping
|
|
47
|
+
self.token_store: dict[int, TokenRecord] = {}
|
|
48
|
+
self.token_to_run_id: dict[str, int] = {}
|
|
49
|
+
self.lock_token_store = Lock()
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def object_store(self) -> ObjectStore:
|
|
53
|
+
"""Return the ObjectStore instance used by this CoreState."""
|
|
54
|
+
return self._object_store
|
|
55
|
+
|
|
56
|
+
def create_token(self, run_id: int) -> str | None:
|
|
57
|
+
"""Create a token for the given run ID."""
|
|
58
|
+
token = secrets.token_hex(FLWR_APP_TOKEN_LENGTH) # Generate a random token
|
|
59
|
+
with self.lock_token_store:
|
|
60
|
+
if run_id in self.token_store:
|
|
61
|
+
return None # Token already created for this run ID
|
|
62
|
+
|
|
63
|
+
self.token_store[run_id] = TokenRecord(
|
|
64
|
+
token=token, active_until=now().timestamp() + HEARTBEAT_DEFAULT_INTERVAL
|
|
65
|
+
)
|
|
66
|
+
self.token_to_run_id[token] = run_id
|
|
67
|
+
return token
|
|
68
|
+
|
|
69
|
+
def verify_token(self, run_id: int, token: str) -> bool:
|
|
70
|
+
"""Verify a token for the given run ID."""
|
|
71
|
+
self._cleanup_expired_tokens()
|
|
72
|
+
with self.lock_token_store:
|
|
73
|
+
record = self.token_store.get(run_id)
|
|
74
|
+
return record is not None and record.token == token
|
|
75
|
+
|
|
76
|
+
def delete_token(self, run_id: int) -> None:
|
|
77
|
+
"""Delete the token for the given run ID."""
|
|
78
|
+
with self.lock_token_store:
|
|
79
|
+
record = self.token_store.pop(run_id, None)
|
|
80
|
+
if record is not None:
|
|
81
|
+
self.token_to_run_id.pop(record.token, None)
|
|
82
|
+
|
|
83
|
+
def get_run_id_by_token(self, token: str) -> int | None:
|
|
84
|
+
"""Get the run ID associated with a given token."""
|
|
85
|
+
self._cleanup_expired_tokens()
|
|
86
|
+
with self.lock_token_store:
|
|
87
|
+
return self.token_to_run_id.get(token)
|
|
88
|
+
|
|
89
|
+
def acknowledge_app_heartbeat(self, token: str) -> bool:
|
|
90
|
+
"""Acknowledge an app heartbeat with the provided token."""
|
|
91
|
+
# Clean up expired tokens
|
|
92
|
+
self._cleanup_expired_tokens()
|
|
93
|
+
|
|
94
|
+
with self.lock_token_store:
|
|
95
|
+
# Return False if token is not found
|
|
96
|
+
if token not in self.token_to_run_id:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
# Get the run_id and update heartbeat info
|
|
100
|
+
run_id = self.token_to_run_id[token]
|
|
101
|
+
record = self.token_store[run_id]
|
|
102
|
+
current = now().timestamp()
|
|
103
|
+
record.active_until = (
|
|
104
|
+
current + HEARTBEAT_PATIENCE * HEARTBEAT_DEFAULT_INTERVAL
|
|
105
|
+
)
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
def _cleanup_expired_tokens(self) -> None:
|
|
109
|
+
"""Remove expired tokens and perform additional cleanup.
|
|
110
|
+
|
|
111
|
+
This method is called before token operations to ensure integrity.
|
|
112
|
+
Subclasses can override `_on_tokens_expired` to add custom cleanup logic.
|
|
113
|
+
"""
|
|
114
|
+
with self.lock_token_store:
|
|
115
|
+
current = now().timestamp()
|
|
116
|
+
expired_records: list[tuple[int, float]] = []
|
|
117
|
+
for run_id, record in list(self.token_store.items()):
|
|
118
|
+
if record.active_until < current:
|
|
119
|
+
expired_records.append((run_id, record.active_until))
|
|
120
|
+
# Remove from both stores
|
|
121
|
+
del self.token_store[run_id]
|
|
122
|
+
self.token_to_run_id.pop(record.token, None)
|
|
123
|
+
|
|
124
|
+
# Hook for subclasses
|
|
125
|
+
if expired_records:
|
|
126
|
+
self._on_tokens_expired(expired_records)
|
|
127
|
+
|
|
128
|
+
def _on_tokens_expired(self, expired_records: list[tuple[int, float]]) -> None:
|
|
129
|
+
"""Handle cleanup of expired tokens.
|
|
130
|
+
|
|
131
|
+
Override in subclasses to add custom cleanup logic.
|
|
132
|
+
|
|
133
|
+
Parameters
|
|
134
|
+
----------
|
|
135
|
+
expired_records : list[tuple[int, float]]
|
|
136
|
+
List of tuples containing (run_id, active_until timestamp)
|
|
137
|
+
for expired tokens.
|
|
138
|
+
"""
|