flwr 1.23.0__py3-none-any.whl → 1.25.0__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 +16 -5
- flwr/app/error.py +2 -2
- flwr/app/exception.py +3 -3
- flwr/cli/app.py +19 -0
- flwr/cli/{new/templates → 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/auth_plugin.py +4 -5
- flwr/cli/auth_plugin/noop_auth_plugin.py +54 -11
- flwr/cli/auth_plugin/oidc_cli_plugin.py +32 -9
- flwr/cli/build.py +60 -18
- flwr/cli/cli_account_auth_interceptor.py +24 -7
- flwr/cli/config_utils.py +101 -13
- flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +10 -1
- flwr/cli/federation/ls.py +140 -0
- flwr/cli/federation/show.py +318 -0
- flwr/cli/install.py +91 -13
- flwr/cli/log.py +52 -9
- flwr/cli/login/login.py +7 -4
- flwr/cli/ls.py +211 -130
- flwr/cli/new/new.py +123 -331
- flwr/cli/pull.py +10 -5
- flwr/cli/run/run.py +71 -29
- flwr/cli/run_utils.py +148 -0
- flwr/cli/stop.py +26 -8
- flwr/cli/supernode/ls.py +25 -12
- flwr/cli/supernode/register.py +9 -4
- flwr/cli/supernode/unregister.py +5 -3
- flwr/cli/utils.py +239 -16
- flwr/client/__init__.py +1 -1
- flwr/client/dpfedavg_numpy_client.py +4 -1
- flwr/client/grpc_adapter_client/connection.py +8 -9
- flwr/client/grpc_rere_client/connection.py +16 -14
- flwr/client/grpc_rere_client/grpc_adapter.py +6 -2
- flwr/client/grpc_rere_client/node_auth_client_interceptor.py +2 -1
- flwr/client/message_handler/message_handler.py +2 -2
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +3 -3
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/connection.py +18 -18
- flwr/client/run_info_store.py +4 -5
- flwr/client/typing.py +1 -1
- flwr/clientapp/client_app.py +9 -10
- flwr/clientapp/mod/centraldp_mods.py +16 -17
- flwr/clientapp/mod/localdp_mod.py +8 -9
- flwr/clientapp/typing.py +1 -1
- flwr/clientapp/utils.py +3 -3
- flwr/common/address.py +1 -2
- flwr/common/args.py +3 -4
- flwr/common/config.py +13 -16
- flwr/common/constant.py +5 -2
- flwr/common/differential_privacy.py +3 -4
- flwr/common/event_log_plugin/event_log_plugin.py +3 -4
- flwr/common/exit/exit.py +15 -2
- flwr/common/exit/exit_code.py +19 -0
- flwr/common/exit/exit_handler.py +6 -2
- flwr/common/exit/signal_handler.py +5 -5
- flwr/common/grpc.py +6 -6
- flwr/common/inflatable_protobuf_utils.py +1 -1
- flwr/common/inflatable_utils.py +38 -21
- flwr/common/logger.py +19 -19
- flwr/common/message.py +4 -4
- flwr/common/object_ref.py +7 -7
- flwr/common/record/array.py +3 -3
- flwr/common/record/arrayrecord.py +18 -30
- 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/ndarrays_arithmetic.py +3 -3
- flwr/common/serde.py +11 -4
- flwr/common/serde_utils.py +2 -2
- flwr/common/telemetry.py +9 -5
- flwr/common/typing.py +58 -37
- flwr/compat/client/app.py +38 -37
- flwr/compat/client/grpc_client/connection.py +11 -11
- flwr/compat/server/app.py +5 -6
- 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 +71 -52
- flwr/proto/control_pb2.pyi +277 -111
- flwr/proto/control_pb2_grpc.py +249 -40
- flwr/proto/control_pb2_grpc.pyi +185 -52
- 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 +14 -4
- flwr/proto/fab_pb2.pyi +59 -31
- 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 +24 -14
- flwr/proto/fleet_pb2.pyi +141 -61
- flwr/proto/fleet_pb2_grpc.py +189 -48
- flwr/proto/fleet_pb2_grpc.pyi +175 -61
- 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 +15 -5
- flwr/proto/node_pb2.pyi +50 -25
- 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 +39 -17
- 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 +10 -12
- 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 +10 -8
- 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 +2 -1
- 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 +4 -4
- flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py +3 -2
- flwr/server/superlink/fleet/message_handler/message_handler.py +75 -30
- flwr/server/superlink/fleet/rest_rere/rest_api.py +2 -2
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +5 -5
- flwr/server/superlink/fleet/vce/vce_api.py +15 -9
- flwr/server/superlink/linkstate/in_memory_linkstate.py +148 -149
- flwr/server/superlink/linkstate/linkstate.py +91 -43
- flwr/server/superlink/linkstate/linkstate_factory.py +22 -5
- flwr/server/superlink/linkstate/sqlite_linkstate.py +502 -436
- flwr/server/superlink/linkstate/utils.py +6 -6
- flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
- flwr/server/superlink/serverappio/serverappio_servicer.py +26 -21
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
- flwr/server/superlink/simulation/simulationio_servicer.py +18 -13
- flwr/server/superlink/utils.py +4 -6
- flwr/server/typing.py +1 -1
- flwr/server/utils/tensorboard.py +15 -8
- flwr/server/workflow/default_workflows.py +5 -5
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +8 -8
- 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 +9 -10
- 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 +9 -9
- 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 +10 -11
- flwr/simulation/ray_transport/ray_client_proxy.py +11 -12
- flwr/simulation/run_simulation.py +43 -43
- flwr/simulation/simulationio_connection.py +4 -4
- flwr/supercore/cli/flower_superexec.py +3 -4
- flwr/supercore/constant.py +34 -1
- flwr/supercore/corestate/corestate.py +24 -3
- flwr/supercore/corestate/in_memory_corestate.py +138 -0
- flwr/supercore/corestate/sqlite_corestate.py +157 -0
- 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 -2
- flwr/supercore/object_store/object_store.py +1 -2
- flwr/supercore/object_store/object_store_factory.py +1 -2
- flwr/supercore/object_store/sqlite_object_store.py +8 -7
- flwr/supercore/primitives/asymmetric.py +1 -1
- flwr/supercore/primitives/asymmetric_ed25519.py +11 -1
- flwr/supercore/sqlite_mixin.py +37 -34
- 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 +190 -0
- flwr/superlink/artifact_provider/artifact_provider.py +1 -2
- flwr/superlink/auth_plugin/auth_plugin.py +6 -9
- flwr/superlink/auth_plugin/noop_auth_plugin.py +6 -9
- flwr/{cli/new/templates/app → superlink/federation}/__init__.py +10 -1
- flwr/superlink/federation/federation_manager.py +64 -0
- flwr/superlink/federation/noop_federation_manager.py +71 -0
- flwr/superlink/servicer/control/control_account_auth_interceptor.py +22 -13
- flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
- flwr/superlink/servicer/control/control_grpc.py +7 -6
- flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
- flwr/superlink/servicer/control/control_servicer.py +190 -23
- flwr/supernode/cli/flower_supernode.py +58 -3
- 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 +41 -22
- flwr/supernode/servicer/clientappio/clientappio_servicer.py +46 -10
- flwr/supernode/start_client_internal.py +165 -46
- {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/METADATA +9 -11
- flwr-1.25.0.dist-info/RECORD +393 -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 +0 -15
- 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/supercore/object_store/utils.py +0 -43
- flwr-1.23.0.dist-info/RECORD +0 -439
- {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/WHEEL +0 -0
- {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/entry_points.txt +0 -0
|
@@ -16,14 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from abc import ABC, abstractmethod
|
|
19
|
-
from typing import Optional
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
class ArtifactProvider(ABC):
|
|
23
22
|
"""ArtifactProvider interface for providing artifact download links."""
|
|
24
23
|
|
|
25
24
|
@abstractmethod
|
|
26
|
-
def get_url(self, run_id: int) ->
|
|
25
|
+
def get_url(self, run_id: int) -> str | None:
|
|
27
26
|
"""Return the artifact download link for the given run ID."""
|
|
28
27
|
|
|
29
28
|
@property
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
from abc import ABC, abstractmethod
|
|
19
19
|
from collections.abc import Sequence
|
|
20
20
|
from pathlib import Path
|
|
21
|
-
from typing import Optional, Union
|
|
22
21
|
|
|
23
22
|
from flwr.common.typing import (
|
|
24
23
|
AccountAuthCredentials,
|
|
@@ -48,25 +47,23 @@ class ControlAuthnPlugin(ABC):
|
|
|
48
47
|
"""Abstract constructor."""
|
|
49
48
|
|
|
50
49
|
@abstractmethod
|
|
51
|
-
def get_login_details(self) ->
|
|
50
|
+
def get_login_details(self) -> AccountAuthLoginDetails | None:
|
|
52
51
|
"""Get the login details."""
|
|
53
52
|
|
|
54
53
|
@abstractmethod
|
|
55
54
|
def validate_tokens_in_metadata(
|
|
56
|
-
self, metadata: Sequence[tuple[str,
|
|
57
|
-
) -> tuple[bool,
|
|
55
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
56
|
+
) -> tuple[bool, AccountInfo | None]:
|
|
58
57
|
"""Validate authentication tokens in the provided metadata."""
|
|
59
58
|
|
|
60
59
|
@abstractmethod
|
|
61
|
-
def get_auth_tokens(self, device_code: str) ->
|
|
60
|
+
def get_auth_tokens(self, device_code: str) -> AccountAuthCredentials | None:
|
|
62
61
|
"""Get authentication tokens."""
|
|
63
62
|
|
|
64
63
|
@abstractmethod
|
|
65
64
|
def refresh_tokens(
|
|
66
|
-
self, metadata: Sequence[tuple[str,
|
|
67
|
-
) -> tuple[
|
|
68
|
-
Optional[Sequence[tuple[str, Union[str, bytes]]]], Optional[AccountInfo]
|
|
69
|
-
]:
|
|
65
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
66
|
+
) -> tuple[Sequence[tuple[str, str | bytes]] | None, AccountInfo | None]:
|
|
70
67
|
"""Refresh authentication tokens in the provided metadata."""
|
|
71
68
|
|
|
72
69
|
|
|
@@ -18,7 +18,6 @@ authorization plugins."""
|
|
|
18
18
|
|
|
19
19
|
from collections.abc import Sequence
|
|
20
20
|
from pathlib import Path
|
|
21
|
-
from typing import Optional, Union
|
|
22
21
|
|
|
23
22
|
from flwr.common.constant import NOOP_ACCOUNT_NAME, NOOP_FLWR_AID, AuthnType
|
|
24
23
|
from flwr.common.typing import (
|
|
@@ -45,7 +44,7 @@ class NoOpControlAuthnPlugin(ControlAuthnPlugin):
|
|
|
45
44
|
):
|
|
46
45
|
pass
|
|
47
46
|
|
|
48
|
-
def get_login_details(self) ->
|
|
47
|
+
def get_login_details(self) -> AccountAuthLoginDetails | None:
|
|
49
48
|
"""Get the login details."""
|
|
50
49
|
# This allows the `flwr login` command to load the NoOp plugin accordingly,
|
|
51
50
|
# which then raises a LoginError when attempting to login.
|
|
@@ -58,20 +57,18 @@ class NoOpControlAuthnPlugin(ControlAuthnPlugin):
|
|
|
58
57
|
)
|
|
59
58
|
|
|
60
59
|
def validate_tokens_in_metadata(
|
|
61
|
-
self, metadata: Sequence[tuple[str,
|
|
62
|
-
) -> tuple[bool,
|
|
60
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
61
|
+
) -> tuple[bool, AccountInfo | None]:
|
|
63
62
|
"""Return valid for no-op plugin."""
|
|
64
63
|
return True, NOOP_ACCOUNT_INFO
|
|
65
64
|
|
|
66
|
-
def get_auth_tokens(self, device_code: str) ->
|
|
65
|
+
def get_auth_tokens(self, device_code: str) -> AccountAuthCredentials | None:
|
|
67
66
|
"""Get authentication tokens."""
|
|
68
67
|
raise RuntimeError("NoOp plugin does not support getting auth tokens.")
|
|
69
68
|
|
|
70
69
|
def refresh_tokens(
|
|
71
|
-
self, metadata: Sequence[tuple[str,
|
|
72
|
-
) -> tuple[
|
|
73
|
-
Optional[Sequence[tuple[str, Union[str, bytes]]]], Optional[AccountInfo]
|
|
74
|
-
]:
|
|
70
|
+
self, metadata: Sequence[tuple[str, str | bytes]]
|
|
71
|
+
) -> tuple[Sequence[tuple[str, str | bytes]] | None, AccountInfo | None]:
|
|
75
72
|
"""Refresh authentication tokens in the provided metadata."""
|
|
76
73
|
return metadata, NOOP_ACCOUNT_INFO
|
|
77
74
|
|
|
@@ -12,4 +12,13 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
# ==============================================================================
|
|
15
|
-
"""
|
|
15
|
+
"""Federation Managers."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .federation_manager import FederationManager
|
|
19
|
+
from .noop_federation_manager import NoOpFederationManager
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"FederationManager",
|
|
23
|
+
"NoOpFederationManager",
|
|
24
|
+
]
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
"""Abstract base class FederationManager."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from abc import ABC, abstractmethod
|
|
19
|
+
from typing import TYPE_CHECKING
|
|
20
|
+
|
|
21
|
+
from flwr.common.typing import Federation
|
|
22
|
+
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from flwr.server.superlink.linkstate.linkstate import LinkState
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class FederationManager(ABC):
|
|
28
|
+
"""Abstract base class for FederationManager."""
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def linkstate(self) -> "LinkState":
|
|
32
|
+
"""Return the LinkState instance."""
|
|
33
|
+
if not (ret := getattr(self, "_linkstate", None)):
|
|
34
|
+
raise RuntimeError("linkstate not set. Assign to linkstate property first.")
|
|
35
|
+
return ret # type: ignore
|
|
36
|
+
|
|
37
|
+
@linkstate.setter
|
|
38
|
+
def linkstate(self, linkstate: "LinkState") -> None:
|
|
39
|
+
"""Set the LinkState instance."""
|
|
40
|
+
self._linkstate = linkstate
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def exists(self, federation: str) -> bool:
|
|
44
|
+
"""Check if a federation exists."""
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def has_member(self, flwr_aid: str, federation: str) -> bool:
|
|
48
|
+
"""Check if the given account is a member of the federation."""
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def filter_nodes(self, node_ids: set[int], federation: str) -> set[int]:
|
|
52
|
+
"""Given a list of node IDs, return sublist with nodes in federation."""
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def has_node(self, node_id: int, federation: str) -> bool:
|
|
56
|
+
"""Given a node ID, check if it is in the federation."""
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def get_federations(self, flwr_aid: str) -> list[str]:
|
|
60
|
+
"""Get federations of which the account is a member."""
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def get_details(self, federation: str) -> Federation:
|
|
64
|
+
"""Get details of the federation."""
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
"""NoOp implementation of FederationManager."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from flwr.common.constant import NOOP_FLWR_AID
|
|
19
|
+
from flwr.common.typing import Federation
|
|
20
|
+
from flwr.supercore.constant import NOOP_FEDERATION
|
|
21
|
+
|
|
22
|
+
from .federation_manager import FederationManager
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class NoOpFederationManager(FederationManager):
|
|
26
|
+
"""No-Op FederationManager implementation."""
|
|
27
|
+
|
|
28
|
+
def exists(self, federation: str) -> bool:
|
|
29
|
+
"""Check if a federation exists."""
|
|
30
|
+
return federation == NOOP_FEDERATION
|
|
31
|
+
|
|
32
|
+
def has_member(self, flwr_aid: str, federation: str) -> bool:
|
|
33
|
+
"""Check if the given account is a member of the federation."""
|
|
34
|
+
if not self.exists(federation):
|
|
35
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
36
|
+
return flwr_aid == NOOP_FLWR_AID
|
|
37
|
+
|
|
38
|
+
def filter_nodes(self, node_ids: set[int], federation: str) -> set[int]:
|
|
39
|
+
"""Given a list of node IDs, return sublist with nodes in federation."""
|
|
40
|
+
if not self.exists(federation):
|
|
41
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
42
|
+
return node_ids
|
|
43
|
+
|
|
44
|
+
def has_node(self, node_id: int, federation: str) -> bool:
|
|
45
|
+
"""Given a node ID, check if it is in the federation."""
|
|
46
|
+
if not self.exists(federation):
|
|
47
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
def get_federations(self, flwr_aid: str) -> list[str]:
|
|
51
|
+
"""Get federations of which the account is a member."""
|
|
52
|
+
if flwr_aid != NOOP_FLWR_AID:
|
|
53
|
+
return []
|
|
54
|
+
return [NOOP_FEDERATION]
|
|
55
|
+
|
|
56
|
+
def get_details(self, federation: str) -> Federation:
|
|
57
|
+
"""Get details of the federation."""
|
|
58
|
+
if federation != NOOP_FEDERATION:
|
|
59
|
+
raise ValueError(f"Federation '{federation}' does not exist.")
|
|
60
|
+
|
|
61
|
+
run_ids = self.linkstate.get_run_ids(flwr_aid=NOOP_FLWR_AID)
|
|
62
|
+
nodes = list(self.linkstate.get_node_info(owner_aids=[NOOP_FLWR_AID]))
|
|
63
|
+
runs = [
|
|
64
|
+
run for run_id in run_ids if (run := self.linkstate.get_run(run_id=run_id))
|
|
65
|
+
]
|
|
66
|
+
return Federation(
|
|
67
|
+
name=NOOP_FEDERATION,
|
|
68
|
+
member_aids=[NOOP_FLWR_AID],
|
|
69
|
+
nodes=nodes,
|
|
70
|
+
runs=runs,
|
|
71
|
+
)
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
import contextvars
|
|
19
|
-
from
|
|
19
|
+
from collections.abc import Callable
|
|
20
|
+
from typing import Any
|
|
20
21
|
|
|
21
22
|
import grpc
|
|
22
23
|
|
|
@@ -33,23 +34,31 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
|
|
|
33
34
|
)
|
|
34
35
|
from flwr.superlink.auth_plugin import ControlAuthnPlugin, ControlAuthzPlugin
|
|
35
36
|
|
|
36
|
-
Request =
|
|
37
|
-
StartRunRequest
|
|
38
|
-
|
|
39
|
-
GetLoginDetailsRequest,
|
|
40
|
-
GetAuthTokensRequest,
|
|
41
|
-
]
|
|
37
|
+
Request = (
|
|
38
|
+
StartRunRequest | StreamLogsRequest | GetLoginDetailsRequest | GetAuthTokensRequest
|
|
39
|
+
)
|
|
42
40
|
|
|
43
|
-
Response =
|
|
44
|
-
StartRunResponse
|
|
45
|
-
|
|
41
|
+
Response = (
|
|
42
|
+
StartRunResponse
|
|
43
|
+
| StreamLogsResponse
|
|
44
|
+
| GetLoginDetailsResponse
|
|
45
|
+
| GetAuthTokensResponse
|
|
46
|
+
)
|
|
46
47
|
|
|
47
48
|
|
|
48
|
-
shared_account_info: contextvars.ContextVar[AccountInfo] =
|
|
49
|
-
"account_info", default=
|
|
49
|
+
shared_account_info: contextvars.ContextVar[AccountInfo | None] = (
|
|
50
|
+
contextvars.ContextVar("account_info", default=None)
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
|
|
54
|
+
def get_current_account_info() -> AccountInfo:
|
|
55
|
+
"""Get the current account info from context, or return a default if not set."""
|
|
56
|
+
account_info = shared_account_info.get()
|
|
57
|
+
if account_info is None:
|
|
58
|
+
return AccountInfo(flwr_aid=None, account_name=None)
|
|
59
|
+
return account_info
|
|
60
|
+
|
|
61
|
+
|
|
53
62
|
class ControlAccountAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
54
63
|
"""Control API interceptor for account authentication."""
|
|
55
64
|
|
|
@@ -93,7 +102,7 @@ class ControlAccountAuthInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
93
102
|
|
|
94
103
|
# Intercept GetLoginDetails and GetAuthTokens requests, and return
|
|
95
104
|
# the response without authentication
|
|
96
|
-
if isinstance(request, (GetLoginDetailsRequest
|
|
105
|
+
if isinstance(request, (GetLoginDetailsRequest | GetAuthTokensRequest)):
|
|
97
106
|
return call(request, context) # type: ignore
|
|
98
107
|
|
|
99
108
|
# For other requests, check if the account is authenticated
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"""Flower Control API event log interceptor."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from collections.abc import Iterator
|
|
19
|
-
from typing import Any,
|
|
18
|
+
from collections.abc import Callable, Iterator
|
|
19
|
+
from typing import Any, cast
|
|
20
20
|
|
|
21
21
|
import grpc
|
|
22
22
|
from google.protobuf.message import Message as GrpcMessage
|
|
@@ -24,7 +24,7 @@ from google.protobuf.message import Message as GrpcMessage
|
|
|
24
24
|
from flwr.common.event_log_plugin.event_log_plugin import EventLogWriterPlugin
|
|
25
25
|
from flwr.common.typing import LogEntry
|
|
26
26
|
|
|
27
|
-
from .control_account_auth_interceptor import
|
|
27
|
+
from .control_account_auth_interceptor import get_current_account_info
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
@@ -60,13 +60,13 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
60
60
|
def _generic_method_handler(
|
|
61
61
|
request: GrpcMessage,
|
|
62
62
|
context: grpc.ServicerContext,
|
|
63
|
-
) ->
|
|
63
|
+
) -> GrpcMessage | Iterator[GrpcMessage] | BaseException:
|
|
64
64
|
log_entry: LogEntry
|
|
65
65
|
# Log before call
|
|
66
66
|
log_entry = self.log_plugin.compose_log_before_event(
|
|
67
67
|
request=request,
|
|
68
68
|
context=context,
|
|
69
|
-
account_info=
|
|
69
|
+
account_info=get_current_account_info(),
|
|
70
70
|
method_name=method_name,
|
|
71
71
|
)
|
|
72
72
|
self.log_plugin.write_log(log_entry)
|
|
@@ -85,7 +85,7 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
85
85
|
log_entry = self.log_plugin.compose_log_after_event(
|
|
86
86
|
request=request,
|
|
87
87
|
context=context,
|
|
88
|
-
account_info=
|
|
88
|
+
account_info=get_current_account_info(),
|
|
89
89
|
method_name=method_name,
|
|
90
90
|
response=unary_response or error,
|
|
91
91
|
)
|
|
@@ -115,7 +115,7 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
115
115
|
log_entry = self.log_plugin.compose_log_after_event(
|
|
116
116
|
request=request,
|
|
117
117
|
context=context,
|
|
118
|
-
account_info=
|
|
118
|
+
account_info=get_current_account_info(),
|
|
119
119
|
method_name=method_name,
|
|
120
120
|
response=stream_response or error,
|
|
121
121
|
)
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
from logging import INFO
|
|
19
|
-
from typing import Optional
|
|
20
19
|
|
|
21
20
|
import grpc
|
|
22
21
|
|
|
@@ -46,7 +45,7 @@ try:
|
|
|
46
45
|
from flwr.ee import get_license_plugin
|
|
47
46
|
except ImportError:
|
|
48
47
|
|
|
49
|
-
def get_license_plugin() ->
|
|
48
|
+
def get_license_plugin() -> LicensePlugin | None:
|
|
50
49
|
"""Return the license plugin."""
|
|
51
50
|
|
|
52
51
|
|
|
@@ -56,15 +55,16 @@ def run_control_api_grpc(
|
|
|
56
55
|
state_factory: LinkStateFactory,
|
|
57
56
|
ffs_factory: FfsFactory,
|
|
58
57
|
objectstore_factory: ObjectStoreFactory,
|
|
59
|
-
certificates:
|
|
58
|
+
certificates: tuple[bytes, bytes, bytes] | None,
|
|
60
59
|
is_simulation: bool,
|
|
61
60
|
authn_plugin: ControlAuthnPlugin,
|
|
62
61
|
authz_plugin: ControlAuthzPlugin,
|
|
63
|
-
event_log_plugin:
|
|
64
|
-
artifact_provider:
|
|
62
|
+
event_log_plugin: EventLogWriterPlugin | None = None,
|
|
63
|
+
artifact_provider: ArtifactProvider | None = None,
|
|
64
|
+
fleet_api_type: str | None = None,
|
|
65
65
|
) -> grpc.Server:
|
|
66
66
|
"""Run Control API (gRPC, request-response)."""
|
|
67
|
-
license_plugin:
|
|
67
|
+
license_plugin: LicensePlugin | None = get_license_plugin()
|
|
68
68
|
if license_plugin and not license_plugin.check_license():
|
|
69
69
|
flwr_exit(ExitCode.SUPERLINK_LICENSE_INVALID)
|
|
70
70
|
|
|
@@ -75,6 +75,7 @@ def run_control_api_grpc(
|
|
|
75
75
|
is_simulation=is_simulation,
|
|
76
76
|
authn_plugin=authn_plugin,
|
|
77
77
|
artifact_provider=artifact_provider,
|
|
78
|
+
fleet_api_type=fleet_api_type,
|
|
78
79
|
)
|
|
79
80
|
interceptors = [ControlAccountAuthInterceptor(authn_plugin, authz_plugin)]
|
|
80
81
|
if license_plugin is not None:
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"""Flower Control API license interceptor."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from collections.abc import Iterator
|
|
19
|
-
from typing import Any
|
|
18
|
+
from collections.abc import Callable, Iterator
|
|
19
|
+
from typing import Any
|
|
20
20
|
|
|
21
21
|
import grpc
|
|
22
22
|
from google.protobuf.message import Message as GrpcMessage
|
|
@@ -57,7 +57,7 @@ class ControlLicenseInterceptor(grpc.ServerInterceptor): # type: ignore
|
|
|
57
57
|
def _generic_method_handler(
|
|
58
58
|
request: GrpcMessage,
|
|
59
59
|
context: grpc.ServicerContext,
|
|
60
|
-
) ->
|
|
60
|
+
) -> GrpcMessage | Iterator[GrpcMessage]:
|
|
61
61
|
"""Handle the method call with license checking."""
|
|
62
62
|
call = method_handler.unary_unary or method_handler.unary_stream
|
|
63
63
|
|