flwr 1.17.0__py3-none-any.whl → 1.19.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 +1 -1
- flwr/app/__init__.py +15 -0
- flwr/app/error.py +68 -0
- flwr/app/metadata.py +223 -0
- flwr/cli/__init__.py +1 -1
- flwr/cli/app.py +21 -2
- flwr/cli/build.py +83 -58
- flwr/cli/cli_user_auth_interceptor.py +1 -1
- flwr/cli/config_utils.py +53 -17
- flwr/cli/example.py +1 -1
- flwr/cli/install.py +1 -1
- flwr/cli/log.py +4 -4
- flwr/cli/login/__init__.py +1 -1
- flwr/cli/login/login.py +15 -8
- flwr/cli/ls.py +16 -37
- flwr/cli/new/__init__.py +1 -1
- flwr/cli/new/new.py +4 -4
- flwr/cli/new/templates/__init__.py +1 -1
- flwr/cli/new/templates/app/__init__.py +1 -1
- flwr/cli/new/templates/app/code/__init__.py +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/__init__.py +1 -1
- flwr/cli/run/run.py +11 -19
- flwr/cli/stop.py +3 -3
- flwr/cli/utils.py +42 -17
- flwr/client/__init__.py +3 -3
- flwr/client/client.py +1 -1
- flwr/client/client_app.py +140 -138
- flwr/client/clientapp/__init__.py +1 -8
- flwr/client/clientapp/utils.py +1 -1
- flwr/client/dpfedavg_numpy_client.py +1 -1
- flwr/client/grpc_adapter_client/__init__.py +1 -1
- flwr/client/grpc_adapter_client/connection.py +5 -5
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +1 -1
- flwr/client/grpc_rere_client/connection.py +131 -61
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +2 -2
- flwr/client/mod/__init__.py +1 -1
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/comms_mods.py +39 -20
- flwr/client/mod/localdp_mod.py +6 -6
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/mod/utils.py +1 -1
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +174 -68
- flwr/client/run_info_store.py +1 -1
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +15 -0
- flwr/common/__init__.py +3 -3
- flwr/common/address.py +1 -1
- flwr/common/args.py +1 -1
- flwr/common/auth_plugin/__init__.py +3 -1
- flwr/common/auth_plugin/auth_plugin.py +30 -4
- flwr/common/config.py +1 -1
- flwr/common/constant.py +37 -8
- flwr/common/context.py +1 -1
- flwr/common/date.py +1 -1
- flwr/common/differential_privacy.py +1 -1
- flwr/common/differential_privacy_constants.py +1 -1
- flwr/common/dp.py +1 -1
- flwr/common/event_log_plugin/event_log_plugin.py +3 -3
- flwr/common/exit/exit.py +6 -6
- flwr/common/exit_handlers.py +31 -1
- flwr/common/grpc.py +1 -1
- flwr/common/heartbeat.py +165 -0
- flwr/common/inflatable.py +290 -0
- flwr/common/inflatable_grpc_utils.py +99 -0
- flwr/common/inflatable_rest_utils.py +99 -0
- flwr/common/inflatable_utils.py +341 -0
- flwr/common/logger.py +1 -1
- flwr/common/message.py +137 -252
- flwr/common/object_ref.py +1 -1
- flwr/common/parameter.py +1 -1
- flwr/common/pyproject.py +1 -1
- flwr/common/record/__init__.py +3 -2
- flwr/common/record/array.py +323 -0
- flwr/common/record/arrayrecord.py +121 -243
- flwr/common/record/configrecord.py +71 -16
- flwr/common/record/conversion_utils.py +2 -2
- flwr/common/record/metricrecord.py +71 -20
- flwr/common/record/recorddict.py +207 -90
- flwr/common/record/typeddict.py +1 -1
- flwr/common/recorddict_compat.py +2 -2
- flwr/common/retry_invoker.py +15 -11
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +52 -30
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
- flwr/common/secure_aggregation/quantization.py +1 -1
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
- flwr/common/serde.py +60 -184
- flwr/common/serde_utils.py +175 -0
- flwr/common/telemetry.py +2 -2
- flwr/common/typing.py +6 -4
- flwr/common/version.py +1 -1
- flwr/compat/__init__.py +15 -0
- flwr/compat/client/__init__.py +15 -0
- flwr/{client → compat/client}/app.py +71 -211
- flwr/{client → compat/client}/grpc_client/__init__.py +1 -1
- flwr/{client → compat/client}/grpc_client/connection.py +13 -13
- flwr/compat/common/__init__.py +15 -0
- flwr/compat/server/__init__.py +15 -0
- flwr/compat/server/app.py +174 -0
- flwr/compat/simulation/__init__.py +15 -0
- flwr/proto/__init__.py +1 -1
- flwr/proto/fleet_pb2.py +32 -27
- flwr/proto/fleet_pb2.pyi +49 -35
- flwr/proto/fleet_pb2_grpc.py +117 -13
- flwr/proto/fleet_pb2_grpc.pyi +47 -6
- flwr/proto/heartbeat_pb2.py +33 -0
- flwr/proto/heartbeat_pb2.pyi +66 -0
- flwr/proto/heartbeat_pb2_grpc.py +4 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +28 -11
- flwr/proto/message_pb2.pyi +125 -0
- flwr/proto/recorddict_pb2.py +16 -28
- flwr/proto/recorddict_pb2.pyi +46 -64
- flwr/proto/run_pb2.py +24 -32
- flwr/proto/run_pb2.pyi +4 -52
- flwr/proto/serverappio_pb2.py +32 -23
- flwr/proto/serverappio_pb2.pyi +45 -3
- flwr/proto/serverappio_pb2_grpc.py +138 -34
- flwr/proto/serverappio_pb2_grpc.pyi +54 -13
- flwr/proto/simulationio_pb2.py +12 -11
- flwr/proto/simulationio_pb2_grpc.py +35 -0
- flwr/proto/simulationio_pb2_grpc.pyi +14 -0
- flwr/server/__init__.py +2 -2
- flwr/server/app.py +69 -187
- flwr/server/client_manager.py +1 -1
- flwr/server/client_proxy.py +1 -1
- flwr/server/compat/__init__.py +1 -1
- flwr/server/compat/app.py +1 -1
- flwr/server/compat/app_utils.py +51 -29
- flwr/server/compat/legacy_context.py +1 -1
- flwr/server/criterion.py +1 -1
- flwr/server/fleet_event_log_interceptor.py +2 -2
- flwr/server/grid/grid.py +3 -3
- flwr/server/grid/grpc_grid.py +104 -34
- flwr/server/grid/inmemory_grid.py +5 -4
- flwr/server/history.py +1 -1
- flwr/server/run_serverapp.py +1 -1
- flwr/server/server.py +1 -1
- flwr/server/server_app.py +65 -58
- flwr/server/server_config.py +1 -1
- flwr/server/serverapp/__init__.py +1 -1
- flwr/server/serverapp/app.py +19 -1
- flwr/server/serverapp_components.py +1 -1
- flwr/server/strategy/__init__.py +1 -1
- flwr/server/strategy/aggregate.py +1 -1
- flwr/server/strategy/bulyan.py +2 -2
- flwr/server/strategy/dp_adaptive_clipping.py +17 -17
- flwr/server/strategy/dp_fixed_clipping.py +17 -17
- flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- flwr/server/strategy/dpfedavg_fixed.py +1 -1
- flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
- flwr/server/strategy/fedadagrad.py +1 -1
- flwr/server/strategy/fedadam.py +1 -1
- flwr/server/strategy/fedavg.py +1 -1
- flwr/server/strategy/fedavg_android.py +1 -1
- flwr/server/strategy/fedavgm.py +1 -1
- flwr/server/strategy/fedmedian.py +1 -1
- flwr/server/strategy/fedopt.py +1 -1
- flwr/server/strategy/fedprox.py +1 -1
- flwr/server/strategy/fedtrimmedavg.py +1 -1
- flwr/server/strategy/fedxgb_bagging.py +1 -1
- flwr/server/strategy/fedxgb_cyclic.py +1 -1
- flwr/server/strategy/fedxgb_nn_avg.py +3 -2
- flwr/server/strategy/fedyogi.py +1 -1
- flwr/server/strategy/krum.py +1 -1
- flwr/server/strategy/qfedavg.py +1 -1
- flwr/server/strategy/strategy.py +1 -1
- flwr/server/superlink/__init__.py +1 -1
- flwr/server/superlink/ffs/__init__.py +3 -1
- flwr/server/superlink/ffs/disk_ffs.py +1 -1
- flwr/server/superlink/ffs/ffs.py +1 -1
- flwr/server/superlink/ffs/ffs_factory.py +1 -1
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
- flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +136 -19
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
- flwr/server/superlink/fleet/vce/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +7 -4
- flwr/server/superlink/linkstate/__init__.py +1 -1
- flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
- flwr/server/superlink/linkstate/linkstate.py +54 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
- flwr/server/superlink/linkstate/utils.py +34 -30
- flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
- flwr/server/superlink/simulation/__init__.py +1 -1
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
- flwr/server/superlink/utils.py +45 -3
- flwr/server/typing.py +1 -1
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +1 -1
- flwr/server/utils/validator.py +3 -3
- flwr/server/workflow/__init__.py +1 -1
- flwr/server/workflow/constant.py +1 -1
- flwr/server/workflow/default_workflows.py +1 -1
- flwr/server/workflow/secure_aggregation/__init__.py +1 -1
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr/serverapp/__init__.py +15 -0
- flwr/simulation/__init__.py +1 -1
- flwr/simulation/app.py +18 -1
- flwr/simulation/legacy_app.py +1 -1
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/ray_transport/utils.py +1 -1
- flwr/simulation/run_simulation.py +2 -2
- flwr/simulation/simulationio_connection.py +1 -1
- flwr/supercore/__init__.py +15 -0
- flwr/supercore/object_store/__init__.py +24 -0
- flwr/supercore/object_store/in_memory_object_store.py +229 -0
- flwr/supercore/object_store/object_store.py +192 -0
- flwr/supercore/object_store/object_store_factory.py +44 -0
- flwr/superexec/__init__.py +1 -1
- flwr/superexec/app.py +1 -1
- flwr/superexec/deployment.py +7 -3
- flwr/superexec/exec_event_log_interceptor.py +4 -4
- flwr/superexec/exec_grpc.py +8 -4
- flwr/superexec/exec_servicer.py +126 -24
- flwr/superexec/exec_user_auth_interceptor.py +38 -9
- flwr/superexec/executor.py +5 -1
- flwr/superexec/simulation.py +8 -2
- flwr/superlink/__init__.py +15 -0
- flwr/{client/supernode → supernode}/__init__.py +1 -8
- flwr/{client/nodestate/nodestate.py → supernode/cli/__init__.py} +8 -15
- flwr/{client/supernode/app.py → supernode/cli/flower_supernode.py} +4 -13
- flwr/supernode/cli/flwr_clientapp.py +81 -0
- flwr/{client → supernode}/nodestate/__init__.py +1 -1
- flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
- flwr/supernode/nodestate/nodestate.py +212 -0
- flwr/{client → supernode}/nodestate/nodestate_factory.py +1 -1
- flwr/supernode/runtime/__init__.py +15 -0
- flwr/{client/clientapp/app.py → supernode/runtime/run_clientapp.py} +26 -57
- flwr/supernode/servicer/__init__.py +15 -0
- flwr/supernode/servicer/clientappio/__init__.py +24 -0
- flwr/{client/clientapp → supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
- flwr/supernode/start_client_internal.py +491 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/METADATA +6 -5
- flwr-1.19.0.dist-info/RECORD +365 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/WHEEL +1 -1
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/entry_points.txt +2 -2
- flwr/client/heartbeat.py +0 -74
- flwr/client/nodestate/in_memory_nodestate.py +0 -38
- flwr-1.17.0.dist-info/LICENSE +0 -202
- flwr-1.17.0.dist-info/RECORD +0 -333
|
@@ -0,0 +1,491 @@
|
|
|
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
|
+
"""Main loop for Flower SuperNode."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import subprocess
|
|
20
|
+
import time
|
|
21
|
+
from collections.abc import Iterator
|
|
22
|
+
from contextlib import contextmanager
|
|
23
|
+
from logging import INFO, WARN
|
|
24
|
+
from os import urandom
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Callable, Optional, Union, cast
|
|
27
|
+
|
|
28
|
+
import grpc
|
|
29
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
30
|
+
from grpc import RpcError
|
|
31
|
+
|
|
32
|
+
from flwr.client.grpc_adapter_client.connection import grpc_adapter
|
|
33
|
+
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
|
|
36
|
+
from flwr.common.config import get_flwr_dir, get_fused_config_from_fab
|
|
37
|
+
from flwr.common.constant import (
|
|
38
|
+
CLIENT_OCTET,
|
|
39
|
+
CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
40
|
+
ISOLATION_MODE_SUBPROCESS,
|
|
41
|
+
MAX_RETRY_DELAY,
|
|
42
|
+
RUN_ID_NUM_BYTES,
|
|
43
|
+
SERVER_OCTET,
|
|
44
|
+
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
45
|
+
TRANSPORT_TYPE_GRPC_RERE,
|
|
46
|
+
TRANSPORT_TYPE_REST,
|
|
47
|
+
TRANSPORT_TYPES,
|
|
48
|
+
)
|
|
49
|
+
from flwr.common.exit import ExitCode, flwr_exit
|
|
50
|
+
from flwr.common.grpc import generic_create_grpc_server
|
|
51
|
+
from flwr.common.logger import log
|
|
52
|
+
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
|
53
|
+
from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
|
|
54
|
+
from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
|
|
55
|
+
from flwr.server.superlink.ffs import Ffs, FfsFactory
|
|
56
|
+
from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
|
|
57
|
+
from flwr.supernode.nodestate import NodeState, NodeStateFactory
|
|
58
|
+
from flwr.supernode.servicer.clientappio import ClientAppInputs, ClientAppIoServicer
|
|
59
|
+
|
|
60
|
+
DEFAULT_FFS_DIR = get_flwr_dir() / "supernode" / "ffs"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# pylint: disable=import-outside-toplevel
|
|
64
|
+
# pylint: disable=too-many-branches
|
|
65
|
+
# pylint: disable=too-many-locals
|
|
66
|
+
# pylint: disable=too-many-statements
|
|
67
|
+
# pylint: disable=too-many-arguments
|
|
68
|
+
def start_client_internal(
|
|
69
|
+
*,
|
|
70
|
+
server_address: str,
|
|
71
|
+
node_config: UserConfig,
|
|
72
|
+
root_certificates: Optional[Union[bytes, str]] = None,
|
|
73
|
+
insecure: Optional[bool] = None,
|
|
74
|
+
transport: str,
|
|
75
|
+
authentication_keys: Optional[
|
|
76
|
+
tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
|
|
77
|
+
] = None,
|
|
78
|
+
max_retries: Optional[int] = None,
|
|
79
|
+
max_wait_time: Optional[float] = None,
|
|
80
|
+
flwr_path: Optional[Path] = None,
|
|
81
|
+
isolation: str = ISOLATION_MODE_SUBPROCESS,
|
|
82
|
+
clientappio_api_address: str = CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
|
83
|
+
) -> None:
|
|
84
|
+
"""Start a Flower client node which connects to a Flower server.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
server_address : str
|
|
89
|
+
The IPv4 or IPv6 address of the server. If the Flower
|
|
90
|
+
server runs on the same machine on port 8080, then `server_address`
|
|
91
|
+
would be `"[::]:8080"`.
|
|
92
|
+
node_config: UserConfig
|
|
93
|
+
The configuration of the node.
|
|
94
|
+
root_certificates : Optional[Union[bytes, str]] (default: None)
|
|
95
|
+
The PEM-encoded root certificates as a byte string or a path string.
|
|
96
|
+
If provided, a secure connection using the certificates will be
|
|
97
|
+
established to an SSL-enabled Flower server.
|
|
98
|
+
insecure : Optional[bool] (default: None)
|
|
99
|
+
Starts an insecure gRPC connection when True. Enables HTTPS connection
|
|
100
|
+
when False, using system certificates if `root_certificates` is None.
|
|
101
|
+
transport : str
|
|
102
|
+
Configure the transport layer. Allowed values:
|
|
103
|
+
- 'grpc-rere': gRPC, request-response
|
|
104
|
+
- 'grpc-adapter': gRPC via 3rd party adapter (experimental)
|
|
105
|
+
- 'rest': HTTP (experimental)
|
|
106
|
+
authentication_keys : Optional[Tuple[PrivateKey, PublicKey]] (default: None)
|
|
107
|
+
Tuple containing the elliptic curve private key and public key for
|
|
108
|
+
authentication from the cryptography library.
|
|
109
|
+
Source: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/
|
|
110
|
+
Used to establish an authenticated connection with the server.
|
|
111
|
+
max_retries: Optional[int] (default: None)
|
|
112
|
+
The maximum number of times the client will try to connect to the
|
|
113
|
+
server before giving up in case of a connection error. If set to None,
|
|
114
|
+
there is no limit to the number of tries.
|
|
115
|
+
max_wait_time: Optional[float] (default: None)
|
|
116
|
+
The maximum duration before the client stops trying to
|
|
117
|
+
connect to the server in case of connection error.
|
|
118
|
+
If set to None, there is no limit to the total time.
|
|
119
|
+
flwr_path: Optional[Path] (default: None)
|
|
120
|
+
The fully resolved path containing installed Flower Apps.
|
|
121
|
+
isolation : str (default: ISOLATION_MODE_SUBPROCESS)
|
|
122
|
+
Isolation mode for `ClientApp`. Possible values are `subprocess` and
|
|
123
|
+
`process`. If `subprocess`, the `ClientApp` runs in a subprocess started
|
|
124
|
+
by the SueprNode and communicates using gRPC at the address
|
|
125
|
+
`clientappio_api_address`. If `process`, the `ClientApp` runs in a separate
|
|
126
|
+
isolated process and communicates using gRPC at the address
|
|
127
|
+
`clientappio_api_address`.
|
|
128
|
+
clientappio_api_address : str
|
|
129
|
+
(default: `CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS`)
|
|
130
|
+
The SuperNode gRPC server address.
|
|
131
|
+
"""
|
|
132
|
+
if insecure is None:
|
|
133
|
+
insecure = root_certificates is None
|
|
134
|
+
|
|
135
|
+
_clientappio_grpc_server, clientappio_servicer = run_clientappio_api_grpc(
|
|
136
|
+
address=clientappio_api_address,
|
|
137
|
+
certificates=None,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Initialize factories
|
|
141
|
+
state_factory = NodeStateFactory()
|
|
142
|
+
ffs_factory = FfsFactory(get_flwr_dir(flwr_path) / "supernode" / "ffs") # type: ignore
|
|
143
|
+
object_store_factory = ObjectStoreFactory()
|
|
144
|
+
|
|
145
|
+
# Initialize NodeState, Ffs, and ObjectStore
|
|
146
|
+
state = state_factory.state()
|
|
147
|
+
ffs = ffs_factory.ffs()
|
|
148
|
+
store = object_store_factory.store()
|
|
149
|
+
|
|
150
|
+
with _init_connection(
|
|
151
|
+
transport=transport,
|
|
152
|
+
server_address=server_address,
|
|
153
|
+
insecure=insecure,
|
|
154
|
+
root_certificates=root_certificates,
|
|
155
|
+
authentication_keys=authentication_keys,
|
|
156
|
+
max_retries=max_retries,
|
|
157
|
+
max_wait_time=max_wait_time,
|
|
158
|
+
) as conn:
|
|
159
|
+
receive, send, create_node, _, get_run, get_fab = conn
|
|
160
|
+
|
|
161
|
+
# Call create_node fn to register node
|
|
162
|
+
# and store node_id in state
|
|
163
|
+
if (node_id := create_node()) is None:
|
|
164
|
+
raise ValueError("Failed to register SuperNode with the SuperLink")
|
|
165
|
+
state.set_node_id(node_id)
|
|
166
|
+
|
|
167
|
+
# pylint: disable=too-many-nested-blocks
|
|
168
|
+
while True:
|
|
169
|
+
# The signature of the function will change after
|
|
170
|
+
# completing the transition to the `NodeState`-based SuperNode
|
|
171
|
+
run_id = _pull_and_store_message(
|
|
172
|
+
state=state,
|
|
173
|
+
ffs=ffs,
|
|
174
|
+
object_store=store,
|
|
175
|
+
node_config=node_config,
|
|
176
|
+
receive=receive,
|
|
177
|
+
get_run=get_run,
|
|
178
|
+
get_fab=get_fab,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if run_id is None:
|
|
182
|
+
time.sleep(3) # Wait for 3s before asking again
|
|
183
|
+
continue
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
# Retrieve message, context, run and fab for this run
|
|
187
|
+
message = state.get_messages(run_ids=[run_id], is_reply=False)[0]
|
|
188
|
+
context = cast(Context, state.get_context(run_id))
|
|
189
|
+
run = cast(Run, state.get_run(run_id))
|
|
190
|
+
fab = Fab(run.fab_hash, ffs.get(run.fab_hash)[0]) # type: ignore
|
|
191
|
+
|
|
192
|
+
# Two isolation modes:
|
|
193
|
+
# 1. `subprocess`: SuperNode is starting the ClientApp
|
|
194
|
+
# process as a subprocess.
|
|
195
|
+
# 2. `process`: ClientApp process gets started separately
|
|
196
|
+
# (via `flwr-clientapp`), for example, in a separate
|
|
197
|
+
# Docker container.
|
|
198
|
+
|
|
199
|
+
# Generate SuperNode token
|
|
200
|
+
token = int.from_bytes(urandom(RUN_ID_NUM_BYTES), "little")
|
|
201
|
+
|
|
202
|
+
# Mode 1: SuperNode starts ClientApp as subprocess
|
|
203
|
+
start_subprocess = isolation == ISOLATION_MODE_SUBPROCESS
|
|
204
|
+
|
|
205
|
+
# Share Message and Context with servicer
|
|
206
|
+
clientappio_servicer.set_inputs(
|
|
207
|
+
clientapp_input=ClientAppInputs(
|
|
208
|
+
message=message,
|
|
209
|
+
context=context,
|
|
210
|
+
run=run,
|
|
211
|
+
fab=fab,
|
|
212
|
+
token=token,
|
|
213
|
+
),
|
|
214
|
+
token_returned=start_subprocess,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
if start_subprocess:
|
|
218
|
+
_octet, _colon, _port = clientappio_api_address.rpartition(":")
|
|
219
|
+
io_address = (
|
|
220
|
+
f"{CLIENT_OCTET}:{_port}"
|
|
221
|
+
if _octet == SERVER_OCTET
|
|
222
|
+
else clientappio_api_address
|
|
223
|
+
)
|
|
224
|
+
# Start ClientApp subprocess
|
|
225
|
+
command = [
|
|
226
|
+
"flwr-clientapp",
|
|
227
|
+
"--clientappio-api-address",
|
|
228
|
+
io_address,
|
|
229
|
+
"--token",
|
|
230
|
+
str(token),
|
|
231
|
+
"--parent-pid",
|
|
232
|
+
str(os.getpid()),
|
|
233
|
+
"--insecure",
|
|
234
|
+
]
|
|
235
|
+
subprocess.run(command, check=False)
|
|
236
|
+
else:
|
|
237
|
+
# Wait for output to become available
|
|
238
|
+
while not clientappio_servicer.has_outputs():
|
|
239
|
+
time.sleep(0.1)
|
|
240
|
+
|
|
241
|
+
outputs = clientappio_servicer.get_outputs()
|
|
242
|
+
reply_message, context = outputs.message, outputs.context
|
|
243
|
+
|
|
244
|
+
# Update context in the state
|
|
245
|
+
state.store_context(context)
|
|
246
|
+
|
|
247
|
+
# Send
|
|
248
|
+
send(reply_message)
|
|
249
|
+
|
|
250
|
+
# Delete messages from the state
|
|
251
|
+
state.delete_messages(
|
|
252
|
+
message_ids=[
|
|
253
|
+
message.metadata.message_id,
|
|
254
|
+
message.metadata.reply_to_message_id,
|
|
255
|
+
]
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
log(INFO, "Sent reply")
|
|
259
|
+
|
|
260
|
+
except RunNotRunningException:
|
|
261
|
+
log(INFO, "")
|
|
262
|
+
log(
|
|
263
|
+
INFO,
|
|
264
|
+
"SuperNode aborted sending the reply message. "
|
|
265
|
+
"Run ID %s is not in `RUNNING` status.",
|
|
266
|
+
run_id,
|
|
267
|
+
)
|
|
268
|
+
log(INFO, "")
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
272
|
+
state: NodeState,
|
|
273
|
+
ffs: Ffs,
|
|
274
|
+
object_store: ObjectStore, # pylint: disable=unused-argument
|
|
275
|
+
node_config: UserConfig,
|
|
276
|
+
receive: Callable[[], Optional[Message]],
|
|
277
|
+
get_run: Callable[[int], Run],
|
|
278
|
+
get_fab: Callable[[str, int], Fab],
|
|
279
|
+
) -> Optional[int]:
|
|
280
|
+
"""Pull a message from the SuperLink and store it in the state.
|
|
281
|
+
|
|
282
|
+
This function current returns None if no message is received,
|
|
283
|
+
or run_id if a message is received and processed successfully.
|
|
284
|
+
This behavior will change in the future to return None after
|
|
285
|
+
completing transition to the `NodeState`-based SuperNode.
|
|
286
|
+
"""
|
|
287
|
+
message = None
|
|
288
|
+
try:
|
|
289
|
+
# Pull message
|
|
290
|
+
if (message := receive()) is None:
|
|
291
|
+
return None
|
|
292
|
+
|
|
293
|
+
# Log message reception
|
|
294
|
+
log(INFO, "")
|
|
295
|
+
if message.metadata.group_id:
|
|
296
|
+
log(
|
|
297
|
+
INFO,
|
|
298
|
+
"[RUN %s, ROUND %s]",
|
|
299
|
+
message.metadata.run_id,
|
|
300
|
+
message.metadata.group_id,
|
|
301
|
+
)
|
|
302
|
+
else:
|
|
303
|
+
log(INFO, "[RUN %s]", message.metadata.run_id)
|
|
304
|
+
log(
|
|
305
|
+
INFO,
|
|
306
|
+
"Received: %s message %s",
|
|
307
|
+
message.metadata.message_type,
|
|
308
|
+
message.metadata.message_id,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Ensure the run and FAB are available
|
|
312
|
+
run_id = message.metadata.run_id
|
|
313
|
+
|
|
314
|
+
# Check if the message is from an unknown run
|
|
315
|
+
if (run_info := state.get_run(run_id)) is None:
|
|
316
|
+
# Pull run info from SuperLink
|
|
317
|
+
run_info = get_run(run_id)
|
|
318
|
+
state.store_run(run_info)
|
|
319
|
+
|
|
320
|
+
# Pull and store the FAB
|
|
321
|
+
fab = get_fab(run_info.fab_hash, run_id)
|
|
322
|
+
ffs.put(fab.content, {})
|
|
323
|
+
|
|
324
|
+
# Initialize the context
|
|
325
|
+
run_cfg = get_fused_config_from_fab(fab.content, run_info)
|
|
326
|
+
run_ctx = Context(
|
|
327
|
+
run_id=run_id,
|
|
328
|
+
node_id=state.get_node_id(),
|
|
329
|
+
node_config=node_config,
|
|
330
|
+
state=RecordDict(),
|
|
331
|
+
run_config=run_cfg,
|
|
332
|
+
)
|
|
333
|
+
state.store_context(run_ctx)
|
|
334
|
+
|
|
335
|
+
# Store the message in the state
|
|
336
|
+
state.store_message(message)
|
|
337
|
+
except RunNotRunningException:
|
|
338
|
+
if message is None:
|
|
339
|
+
log(
|
|
340
|
+
INFO,
|
|
341
|
+
"Run transitioned to a non-`RUNNING` status while receiving a message. "
|
|
342
|
+
"Ignoring the message.",
|
|
343
|
+
)
|
|
344
|
+
else:
|
|
345
|
+
log(
|
|
346
|
+
INFO,
|
|
347
|
+
"Run ID %s is not in `RUNNING` status. Ignoring message %s.",
|
|
348
|
+
run_id,
|
|
349
|
+
message.metadata.message_id,
|
|
350
|
+
)
|
|
351
|
+
return None
|
|
352
|
+
|
|
353
|
+
return run_id
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
@contextmanager
|
|
357
|
+
def _init_connection( # pylint: disable=too-many-positional-arguments
|
|
358
|
+
transport: str,
|
|
359
|
+
server_address: str,
|
|
360
|
+
insecure: bool,
|
|
361
|
+
root_certificates: Optional[Union[bytes, str]] = None,
|
|
362
|
+
authentication_keys: Optional[
|
|
363
|
+
tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
|
|
364
|
+
] = None,
|
|
365
|
+
max_retries: Optional[int] = None,
|
|
366
|
+
max_wait_time: Optional[float] = None,
|
|
367
|
+
) -> Iterator[
|
|
368
|
+
tuple[
|
|
369
|
+
Callable[[], Optional[Message]],
|
|
370
|
+
Callable[[Message], None],
|
|
371
|
+
Callable[[], Optional[int]],
|
|
372
|
+
Callable[[], None],
|
|
373
|
+
Callable[[int], Run],
|
|
374
|
+
Callable[[str, int], Fab],
|
|
375
|
+
]
|
|
376
|
+
]:
|
|
377
|
+
"""Establish a connection to the Fleet API server at SuperLink."""
|
|
378
|
+
# Parse IP address
|
|
379
|
+
parsed_address = parse_address(server_address)
|
|
380
|
+
if not parsed_address:
|
|
381
|
+
flwr_exit(
|
|
382
|
+
ExitCode.COMMON_ADDRESS_INVALID,
|
|
383
|
+
f"SuperLink address ({server_address}) cannot be parsed.",
|
|
384
|
+
)
|
|
385
|
+
host, port, is_v6 = parsed_address
|
|
386
|
+
address = f"[{host}]:{port}" if is_v6 else f"{host}:{port}"
|
|
387
|
+
|
|
388
|
+
# Use either gRPC bidirectional streaming or REST request/response
|
|
389
|
+
if transport == TRANSPORT_TYPE_REST:
|
|
390
|
+
try:
|
|
391
|
+
from requests.exceptions import ConnectionError as RequestsConnectionError
|
|
392
|
+
|
|
393
|
+
from flwr.client.rest_client.connection import http_request_response
|
|
394
|
+
except ModuleNotFoundError:
|
|
395
|
+
flwr_exit(ExitCode.COMMON_MISSING_EXTRA_REST)
|
|
396
|
+
if server_address[:4] != "http":
|
|
397
|
+
flwr_exit(ExitCode.SUPERNODE_REST_ADDRESS_INVALID)
|
|
398
|
+
connection, error_type = http_request_response, RequestsConnectionError
|
|
399
|
+
elif transport == TRANSPORT_TYPE_GRPC_RERE:
|
|
400
|
+
connection, error_type = grpc_request_response, RpcError
|
|
401
|
+
elif transport == TRANSPORT_TYPE_GRPC_ADAPTER:
|
|
402
|
+
connection, error_type = grpc_adapter, RpcError
|
|
403
|
+
else:
|
|
404
|
+
raise ValueError(
|
|
405
|
+
f"Unknown transport type: {transport} (possible: {TRANSPORT_TYPES})"
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# Create RetryInvoker
|
|
409
|
+
retry_invoker = _make_fleet_connection_retry_invoker(
|
|
410
|
+
max_retries=max_retries,
|
|
411
|
+
max_wait_time=max_wait_time,
|
|
412
|
+
connection_error_type=error_type,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# Establish connection
|
|
416
|
+
with connection(
|
|
417
|
+
address,
|
|
418
|
+
insecure,
|
|
419
|
+
retry_invoker,
|
|
420
|
+
GRPC_MAX_MESSAGE_LENGTH,
|
|
421
|
+
root_certificates,
|
|
422
|
+
authentication_keys,
|
|
423
|
+
) as conn:
|
|
424
|
+
yield conn
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _make_fleet_connection_retry_invoker(
|
|
428
|
+
max_retries: Optional[int] = None,
|
|
429
|
+
max_wait_time: Optional[float] = None,
|
|
430
|
+
connection_error_type: type[Exception] = RpcError,
|
|
431
|
+
) -> RetryInvoker:
|
|
432
|
+
"""Create a retry invoker for fleet connection."""
|
|
433
|
+
|
|
434
|
+
def _on_success(retry_state: RetryState) -> None:
|
|
435
|
+
if retry_state.tries > 1:
|
|
436
|
+
log(
|
|
437
|
+
INFO,
|
|
438
|
+
"Connection successful after %.2f seconds and %s tries.",
|
|
439
|
+
retry_state.elapsed_time,
|
|
440
|
+
retry_state.tries,
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
def _on_backoff(retry_state: RetryState) -> None:
|
|
444
|
+
if retry_state.tries == 1:
|
|
445
|
+
log(WARN, "Connection attempt failed, retrying...")
|
|
446
|
+
else:
|
|
447
|
+
log(
|
|
448
|
+
WARN,
|
|
449
|
+
"Connection attempt failed, retrying in %.2f seconds",
|
|
450
|
+
retry_state.actual_wait,
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
return RetryInvoker(
|
|
454
|
+
wait_gen_factory=lambda: exponential(max_delay=MAX_RETRY_DELAY),
|
|
455
|
+
recoverable_exceptions=connection_error_type,
|
|
456
|
+
max_tries=max_retries + 1 if max_retries is not None else None,
|
|
457
|
+
max_time=max_wait_time,
|
|
458
|
+
on_giveup=lambda retry_state: (
|
|
459
|
+
log(
|
|
460
|
+
WARN,
|
|
461
|
+
"Giving up reconnection after %.2f seconds and %s tries.",
|
|
462
|
+
retry_state.elapsed_time,
|
|
463
|
+
retry_state.tries,
|
|
464
|
+
)
|
|
465
|
+
if retry_state.tries > 1
|
|
466
|
+
else None
|
|
467
|
+
),
|
|
468
|
+
on_success=_on_success,
|
|
469
|
+
on_backoff=_on_backoff,
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def run_clientappio_api_grpc(
|
|
474
|
+
address: str,
|
|
475
|
+
certificates: Optional[tuple[bytes, bytes, bytes]],
|
|
476
|
+
) -> tuple[grpc.Server, ClientAppIoServicer]:
|
|
477
|
+
"""Run ClientAppIo API gRPC server."""
|
|
478
|
+
clientappio_servicer: grpc.Server = ClientAppIoServicer()
|
|
479
|
+
clientappio_add_servicer_to_server_fn = add_ClientAppIoServicer_to_server
|
|
480
|
+
clientappio_grpc_server = generic_create_grpc_server(
|
|
481
|
+
servicer_and_add_fn=(
|
|
482
|
+
clientappio_servicer,
|
|
483
|
+
clientappio_add_servicer_to_server_fn,
|
|
484
|
+
),
|
|
485
|
+
server_address=address,
|
|
486
|
+
max_message_length=GRPC_MAX_MESSAGE_LENGTH,
|
|
487
|
+
certificates=certificates,
|
|
488
|
+
)
|
|
489
|
+
log(INFO, "Starting Flower ClientAppIo gRPC server on %s", address)
|
|
490
|
+
clientappio_grpc_server.start()
|
|
491
|
+
return clientappio_grpc_server, clientappio_servicer
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: flwr
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.19.0
|
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
|
5
|
-
Home-page: https://flower.ai
|
|
6
5
|
License: Apache-2.0
|
|
7
6
|
Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
|
|
8
7
|
Author: The Flower Authors
|
|
@@ -19,8 +18,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
24
23
|
Classifier: Programming Language :: Python :: 3.9
|
|
25
24
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
26
25
|
Classifier: Topic :: Scientific/Engineering
|
|
@@ -32,6 +31,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
32
31
|
Classifier: Typing :: Typed
|
|
33
32
|
Provides-Extra: rest
|
|
34
33
|
Provides-Extra: simulation
|
|
34
|
+
Requires-Dist: click (<8.2.0)
|
|
35
35
|
Requires-Dist: cryptography (>=44.0.1,<45.0.0)
|
|
36
36
|
Requires-Dist: grpcio (>=1.62.3,<2.0.0,!=1.65.0)
|
|
37
37
|
Requires-Dist: iterators (>=0.0.2,<0.0.3)
|
|
@@ -49,6 +49,7 @@ Requires-Dist: tomli-w (>=1.0.0,<2.0.0)
|
|
|
49
49
|
Requires-Dist: typer (>=0.12.5,<0.13.0)
|
|
50
50
|
Requires-Dist: uvicorn[standard] (>=0.34.0,<0.35.0) ; extra == "rest"
|
|
51
51
|
Project-URL: Documentation, https://flower.ai
|
|
52
|
+
Project-URL: Homepage, https://flower.ai
|
|
52
53
|
Project-URL: Repository, https://github.com/adap/flower
|
|
53
54
|
Description-Content-Type: text/markdown
|
|
54
55
|
|
|
@@ -56,7 +57,7 @@ Description-Content-Type: text/markdown
|
|
|
56
57
|
|
|
57
58
|
<p align="center">
|
|
58
59
|
<a href="https://flower.ai/">
|
|
59
|
-
<img src="https://flower.ai/_next/image/?url=%2F_next%2Fstatic%2Fmedia%
|
|
60
|
+
<img src="https://flower.ai/_next/image/?url=%2F_next%2Fstatic%2Fmedia%2Fflwr-head.4d68867a.png&w=384&q=75" width="140px" alt="Flower Website" />
|
|
60
61
|
</a>
|
|
61
62
|
</p>
|
|
62
63
|
<p align="center">
|