flwr-nightly 1.10.0.dev20240612__py3-none-any.whl → 1.10.0.dev20240624__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.
Potentially problematic release.
This version of flwr-nightly might be problematic. Click here for more details.
- flwr/cli/app.py +3 -0
- flwr/cli/build.py +6 -8
- flwr/cli/config_utils.py +53 -3
- flwr/cli/install.py +35 -20
- flwr/cli/new/new.py +104 -28
- flwr/cli/new/templates/app/README.flowertune.md.tpl +56 -0
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
- flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +86 -0
- flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl +124 -0
- flwr/cli/new/templates/app/code/flwr_tune/config.yaml.tpl +34 -0
- flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +57 -0
- flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +59 -0
- flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +48 -0
- flwr/cli/new/templates/app/code/flwr_tune/static_config.yaml.tpl +11 -0
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +42 -0
- flwr/cli/run/run.py +46 -2
- flwr/client/__init__.py +1 -1
- flwr/client/app.py +22 -10
- flwr/client/client_app.py +1 -1
- flwr/client/dpfedavg_numpy_client.py +1 -1
- flwr/client/grpc_adapter_client/__init__.py +15 -0
- flwr/client/grpc_adapter_client/connection.py +94 -0
- flwr/client/grpc_client/connection.py +5 -1
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/connection.py +9 -2
- flwr/client/grpc_rere_client/grpc_adapter.py +133 -0
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +1 -1
- flwr/client/mod/__init__.py +4 -4
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/utils.py +1 -1
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +10 -2
- flwr/client/supernode/app.py +141 -41
- flwr/common/__init__.py +12 -12
- flwr/common/address.py +1 -1
- flwr/common/config.py +73 -0
- flwr/common/constant.py +16 -1
- flwr/common/date.py +1 -1
- flwr/common/dp.py +1 -1
- flwr/common/grpc.py +1 -1
- flwr/common/object_ref.py +39 -5
- flwr/common/record/__init__.py +1 -1
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +1 -1
- 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/telemetry.py +4 -0
- flwr/common/typing.py +9 -0
- flwr/common/version.py +14 -0
- flwr/proto/exec_pb2.py +34 -0
- flwr/proto/exec_pb2.pyi +55 -0
- flwr/proto/exec_pb2_grpc.py +101 -0
- flwr/proto/exec_pb2_grpc.pyi +41 -0
- flwr/proto/fab_pb2.py +30 -0
- flwr/proto/fab_pb2.pyi +56 -0
- flwr/proto/fab_pb2_grpc.py +4 -0
- flwr/proto/fab_pb2_grpc.pyi +4 -0
- flwr/server/__init__.py +2 -2
- flwr/server/app.py +62 -25
- flwr/server/compat/app.py +1 -1
- flwr/server/compat/app_utils.py +1 -1
- flwr/server/compat/driver_client_proxy.py +1 -1
- flwr/server/driver/driver.py +6 -0
- flwr/server/driver/grpc_driver.py +85 -63
- flwr/server/driver/inmemory_driver.py +28 -26
- flwr/server/run_serverapp.py +65 -20
- flwr/server/strategy/__init__.py +2 -2
- flwr/server/strategy/bulyan.py +1 -1
- flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- flwr/server/strategy/dpfedavg_fixed.py +1 -1
- flwr/server/strategy/fedadagrad.py +1 -1
- flwr/server/strategy/fedadam.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/fedxgb_bagging.py +1 -1
- flwr/server/strategy/fedxgb_cyclic.py +1 -1
- flwr/server/strategy/fedxgb_nn_avg.py +1 -1
- flwr/server/strategy/fedyogi.py +1 -1
- flwr/server/strategy/krum.py +1 -1
- flwr/server/strategy/qfedavg.py +1 -1
- flwr/server/superlink/driver/__init__.py +1 -1
- flwr/server/superlink/driver/driver_grpc.py +1 -1
- flwr/server/superlink/driver/driver_servicer.py +15 -3
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +131 -0
- 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 +5 -1
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +1 -1
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +4 -4
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +44 -25
- flwr/server/superlink/fleet/vce/vce_api.py +3 -1
- flwr/server/superlink/state/__init__.py +1 -1
- flwr/server/superlink/state/in_memory_state.py +9 -6
- flwr/server/superlink/state/sqlite_state.py +7 -4
- flwr/server/superlink/state/state.py +6 -5
- flwr/server/superlink/state/state_factory.py +11 -2
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +1 -1
- flwr/simulation/__init__.py +5 -2
- flwr/simulation/app.py +1 -1
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +0 -6
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/run_simulation.py +63 -22
- flwr/superexec/__init__.py +21 -0
- flwr/superexec/app.py +178 -0
- flwr/superexec/exec_grpc.py +51 -0
- flwr/superexec/exec_servicer.py +65 -0
- flwr/superexec/executor.py +54 -0
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240624.dist-info}/METADATA +2 -1
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240624.dist-info}/RECORD +130 -101
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240624.dist-info}/entry_points.txt +1 -0
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240624.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.10.0.dev20240612.dist-info → flwr_nightly-1.10.0.dev20240624.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Copyright 2024 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
|
+
"""Contextmanager for a GrpcAdapter channel to the Flower server."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from contextlib import contextmanager
|
|
19
|
+
from logging import ERROR
|
|
20
|
+
from typing import Callable, Iterator, Optional, Tuple, Union
|
|
21
|
+
|
|
22
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
23
|
+
|
|
24
|
+
from flwr.client.grpc_rere_client.connection import grpc_request_response
|
|
25
|
+
from flwr.client.grpc_rere_client.grpc_adapter import GrpcAdapter
|
|
26
|
+
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
27
|
+
from flwr.common.logger import log
|
|
28
|
+
from flwr.common.message import Message
|
|
29
|
+
from flwr.common.retry_invoker import RetryInvoker
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@contextmanager
|
|
33
|
+
def grpc_adapter( # pylint: disable=R0913
|
|
34
|
+
server_address: str,
|
|
35
|
+
insecure: bool,
|
|
36
|
+
retry_invoker: RetryInvoker,
|
|
37
|
+
max_message_length: int = GRPC_MAX_MESSAGE_LENGTH, # pylint: disable=W0613
|
|
38
|
+
root_certificates: Optional[Union[bytes, str]] = None,
|
|
39
|
+
authentication_keys: Optional[ # pylint: disable=unused-argument
|
|
40
|
+
Tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
|
|
41
|
+
] = None,
|
|
42
|
+
) -> Iterator[
|
|
43
|
+
Tuple[
|
|
44
|
+
Callable[[], Optional[Message]],
|
|
45
|
+
Callable[[Message], None],
|
|
46
|
+
Optional[Callable[[], None]],
|
|
47
|
+
Optional[Callable[[], None]],
|
|
48
|
+
Optional[Callable[[int], Tuple[str, str]]],
|
|
49
|
+
]
|
|
50
|
+
]:
|
|
51
|
+
"""Primitives for request/response-based interaction with a server via GrpcAdapter.
|
|
52
|
+
|
|
53
|
+
Parameters
|
|
54
|
+
----------
|
|
55
|
+
server_address : str
|
|
56
|
+
The IPv6 address of the server with `http://` or `https://`.
|
|
57
|
+
If the Flower server runs on the same machine
|
|
58
|
+
on port 8080, then `server_address` would be `"http://[::]:8080"`.
|
|
59
|
+
insecure : bool
|
|
60
|
+
Starts an insecure gRPC connection when True. Enables HTTPS connection
|
|
61
|
+
when False, using system certificates if `root_certificates` is None.
|
|
62
|
+
retry_invoker: RetryInvoker
|
|
63
|
+
`RetryInvoker` object that will try to reconnect the client to the server
|
|
64
|
+
after gRPC errors. If None, the client will only try to
|
|
65
|
+
reconnect once after a failure.
|
|
66
|
+
max_message_length : int
|
|
67
|
+
Ignored, only present to preserve API-compatibility.
|
|
68
|
+
root_certificates : Optional[Union[bytes, str]] (default: None)
|
|
69
|
+
Path of the root certificate. If provided, a secure
|
|
70
|
+
connection using the certificates will be established to an SSL-enabled
|
|
71
|
+
Flower server. Bytes won't work for the REST API.
|
|
72
|
+
authentication_keys : Optional[Tuple[PrivateKey, PublicKey]] (default: None)
|
|
73
|
+
Client authentication is not supported for this transport type.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
receive : Callable
|
|
78
|
+
send : Callable
|
|
79
|
+
create_node : Optional[Callable]
|
|
80
|
+
delete_node : Optional[Callable]
|
|
81
|
+
get_run : Optional[Callable]
|
|
82
|
+
"""
|
|
83
|
+
if authentication_keys is not None:
|
|
84
|
+
log(ERROR, "Client authentication is not supported for this transport type.")
|
|
85
|
+
with grpc_request_response(
|
|
86
|
+
server_address=server_address,
|
|
87
|
+
insecure=insecure,
|
|
88
|
+
retry_invoker=retry_invoker,
|
|
89
|
+
max_message_length=max_message_length,
|
|
90
|
+
root_certificates=root_certificates,
|
|
91
|
+
authentication_keys=None, # Authentication is not supported
|
|
92
|
+
adapter_cls=GrpcAdapter,
|
|
93
|
+
) as conn:
|
|
94
|
+
yield conn
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
import uuid
|
|
19
19
|
from contextlib import contextmanager
|
|
20
|
-
from logging import DEBUG
|
|
20
|
+
from logging import DEBUG, ERROR
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from queue import Queue
|
|
23
23
|
from typing import Callable, Iterator, Optional, Tuple, Union, cast
|
|
@@ -101,6 +101,8 @@ def grpc_connection( # pylint: disable=R0913, R0915
|
|
|
101
101
|
The PEM-encoded root certificates as a byte string or a path string.
|
|
102
102
|
If provided, a secure connection using the certificates will be
|
|
103
103
|
established to an SSL-enabled Flower server.
|
|
104
|
+
authentication_keys : Optional[Tuple[PrivateKey, PublicKey]] (default: None)
|
|
105
|
+
Client authentication is not supported for this transport type.
|
|
104
106
|
|
|
105
107
|
Returns
|
|
106
108
|
-------
|
|
@@ -123,6 +125,8 @@ def grpc_connection( # pylint: disable=R0913, R0915
|
|
|
123
125
|
"""
|
|
124
126
|
if isinstance(root_certificates, str):
|
|
125
127
|
root_certificates = Path(root_certificates).read_bytes()
|
|
128
|
+
if authentication_keys is not None:
|
|
129
|
+
log(ERROR, "Client authentication is not supported for this transport type.")
|
|
126
130
|
|
|
127
131
|
channel = create_channel(
|
|
128
132
|
server_address=server_address,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2023 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -55,6 +55,7 @@ from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=
|
|
|
55
55
|
from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
|
|
56
56
|
|
|
57
57
|
from .client_interceptor import AuthenticateClientInterceptor
|
|
58
|
+
from .grpc_adapter import GrpcAdapter
|
|
58
59
|
|
|
59
60
|
|
|
60
61
|
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
@@ -72,7 +73,7 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
|
|
|
72
73
|
authentication_keys: Optional[
|
|
73
74
|
Tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
|
|
74
75
|
] = None,
|
|
75
|
-
adapter_cls: Optional[Type[FleetStub]] = None,
|
|
76
|
+
adapter_cls: Optional[Union[Type[FleetStub], Type[GrpcAdapter]]] = None,
|
|
76
77
|
) -> Iterator[
|
|
77
78
|
Tuple[
|
|
78
79
|
Callable[[], Optional[Message]],
|
|
@@ -106,6 +107,11 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
|
|
|
106
107
|
Path of the root certificate. If provided, a secure
|
|
107
108
|
connection using the certificates will be established to an SSL-enabled
|
|
108
109
|
Flower server. Bytes won't work for the REST API.
|
|
110
|
+
authentication_keys : Optional[Tuple[PrivateKey, PublicKey]] (default: None)
|
|
111
|
+
Tuple containing the elliptic curve private key and public key for
|
|
112
|
+
authentication from the cryptography library.
|
|
113
|
+
Source: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/
|
|
114
|
+
Used to establish an authenticated connection with the server.
|
|
109
115
|
|
|
110
116
|
Returns
|
|
111
117
|
-------
|
|
@@ -113,6 +119,7 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
|
|
|
113
119
|
send : Callable
|
|
114
120
|
create_node : Optional[Callable]
|
|
115
121
|
delete_node : Optional[Callable]
|
|
122
|
+
get_run : Optional[Callable]
|
|
116
123
|
"""
|
|
117
124
|
if isinstance(root_certificates, str):
|
|
118
125
|
root_certificates = Path(root_certificates).read_bytes()
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Copyright 2024 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
|
+
"""GrpcAdapter implementation."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
from logging import DEBUG
|
|
20
|
+
from typing import Any, Type, TypeVar, cast
|
|
21
|
+
|
|
22
|
+
import grpc
|
|
23
|
+
from google.protobuf.message import Message as GrpcMessage
|
|
24
|
+
|
|
25
|
+
from flwr.common import log
|
|
26
|
+
from flwr.common.constant import (
|
|
27
|
+
GRPC_ADAPTER_METADATA_FLOWER_VERSION_KEY,
|
|
28
|
+
GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY,
|
|
29
|
+
)
|
|
30
|
+
from flwr.common.version import package_version
|
|
31
|
+
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
32
|
+
CreateNodeRequest,
|
|
33
|
+
CreateNodeResponse,
|
|
34
|
+
DeleteNodeRequest,
|
|
35
|
+
DeleteNodeResponse,
|
|
36
|
+
PingRequest,
|
|
37
|
+
PingResponse,
|
|
38
|
+
PullTaskInsRequest,
|
|
39
|
+
PullTaskInsResponse,
|
|
40
|
+
PushTaskResRequest,
|
|
41
|
+
PushTaskResResponse,
|
|
42
|
+
)
|
|
43
|
+
from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
|
|
44
|
+
from flwr.proto.grpcadapter_pb2_grpc import GrpcAdapterStub
|
|
45
|
+
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
|
46
|
+
|
|
47
|
+
T = TypeVar("T", bound=GrpcMessage)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class GrpcAdapter:
|
|
51
|
+
"""Adapter class to send and receive gRPC messages via the ``GrpcAdapterStub``.
|
|
52
|
+
|
|
53
|
+
This class utilizes the ``GrpcAdapterStub`` to send and receive gRPC messages
|
|
54
|
+
which are defined and used by the Fleet API, as defined in ``fleet.proto``.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, channel: grpc.Channel) -> None:
|
|
58
|
+
self.stub = GrpcAdapterStub(channel)
|
|
59
|
+
|
|
60
|
+
def _send_and_receive(
|
|
61
|
+
self, request: GrpcMessage, response_type: Type[T], **kwargs: Any
|
|
62
|
+
) -> T:
|
|
63
|
+
# Serialize request
|
|
64
|
+
container_req = MessageContainer(
|
|
65
|
+
metadata={GRPC_ADAPTER_METADATA_FLOWER_VERSION_KEY: package_version},
|
|
66
|
+
grpc_message_name=request.__class__.__qualname__,
|
|
67
|
+
grpc_message_content=request.SerializeToString(),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Send via the stub
|
|
71
|
+
container_res = cast(
|
|
72
|
+
MessageContainer, self.stub.SendReceive(container_req, **kwargs)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Handle control message
|
|
76
|
+
should_exit = (
|
|
77
|
+
container_res.metadata.get(GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY, "false")
|
|
78
|
+
== "true"
|
|
79
|
+
)
|
|
80
|
+
if should_exit:
|
|
81
|
+
log(
|
|
82
|
+
DEBUG,
|
|
83
|
+
'Received shutdown signal: exit flag is set to ``"true"``. Exiting...',
|
|
84
|
+
)
|
|
85
|
+
sys.exit(0)
|
|
86
|
+
|
|
87
|
+
# Check the grpc_message_name of the response
|
|
88
|
+
if container_res.grpc_message_name != response_type.__qualname__:
|
|
89
|
+
raise ValueError(
|
|
90
|
+
f"Invalid grpc_message_name. Expected {response_type.__qualname__}"
|
|
91
|
+
f", but got {container_res.grpc_message_name}."
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Deserialize response
|
|
95
|
+
response = response_type()
|
|
96
|
+
response.ParseFromString(container_res.grpc_message_content)
|
|
97
|
+
return response
|
|
98
|
+
|
|
99
|
+
def CreateNode( # pylint: disable=C0103
|
|
100
|
+
self, request: CreateNodeRequest, **kwargs: Any
|
|
101
|
+
) -> CreateNodeResponse:
|
|
102
|
+
"""."""
|
|
103
|
+
return self._send_and_receive(request, CreateNodeResponse, **kwargs)
|
|
104
|
+
|
|
105
|
+
def DeleteNode( # pylint: disable=C0103
|
|
106
|
+
self, request: DeleteNodeRequest, **kwargs: Any
|
|
107
|
+
) -> DeleteNodeResponse:
|
|
108
|
+
"""."""
|
|
109
|
+
return self._send_and_receive(request, DeleteNodeResponse, **kwargs)
|
|
110
|
+
|
|
111
|
+
def Ping( # pylint: disable=C0103
|
|
112
|
+
self, request: PingRequest, **kwargs: Any
|
|
113
|
+
) -> PingResponse:
|
|
114
|
+
"""."""
|
|
115
|
+
return self._send_and_receive(request, PingResponse, **kwargs)
|
|
116
|
+
|
|
117
|
+
def PullTaskIns( # pylint: disable=C0103
|
|
118
|
+
self, request: PullTaskInsRequest, **kwargs: Any
|
|
119
|
+
) -> PullTaskInsResponse:
|
|
120
|
+
"""."""
|
|
121
|
+
return self._send_and_receive(request, PullTaskInsResponse, **kwargs)
|
|
122
|
+
|
|
123
|
+
def PushTaskRes( # pylint: disable=C0103
|
|
124
|
+
self, request: PushTaskResRequest, **kwargs: Any
|
|
125
|
+
) -> PushTaskResResponse:
|
|
126
|
+
"""."""
|
|
127
|
+
return self._send_and_receive(request, PushTaskResResponse, **kwargs)
|
|
128
|
+
|
|
129
|
+
def GetRun( # pylint: disable=C0103
|
|
130
|
+
self, request: GetRunRequest, **kwargs: Any
|
|
131
|
+
) -> GetRunResponse:
|
|
132
|
+
"""."""
|
|
133
|
+
return self._send_and_receive(request, GetRunResponse, **kwargs)
|
flwr/client/mod/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2024 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -22,12 +22,12 @@ from .secure_aggregation import secagg_mod, secaggplus_mod
|
|
|
22
22
|
from .utils import make_ffn
|
|
23
23
|
|
|
24
24
|
__all__ = [
|
|
25
|
+
"LocalDpMod",
|
|
25
26
|
"adaptiveclipping_mod",
|
|
26
27
|
"fixedclipping_mod",
|
|
27
|
-
"LocalDpMod",
|
|
28
28
|
"make_ffn",
|
|
29
|
-
"secagg_mod",
|
|
30
|
-
"secaggplus_mod",
|
|
31
29
|
"message_size_mod",
|
|
32
30
|
"parameters_size_mod",
|
|
31
|
+
"secagg_mod",
|
|
32
|
+
"secaggplus_mod",
|
|
33
33
|
]
|
flwr/client/mod/utils.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
1
|
+
# Copyright 2023 Flower Labs GmbH. All Rights Reserved.
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -117,10 +117,16 @@ def http_request_response( # pylint: disable=,R0913, R0914, R0915
|
|
|
117
117
|
Path of the root certificate. If provided, a secure
|
|
118
118
|
connection using the certificates will be established to an SSL-enabled
|
|
119
119
|
Flower server. Bytes won't work for the REST API.
|
|
120
|
+
authentication_keys : Optional[Tuple[PrivateKey, PublicKey]] (default: None)
|
|
121
|
+
Client authentication is not supported for this transport type.
|
|
120
122
|
|
|
121
123
|
Returns
|
|
122
124
|
-------
|
|
123
|
-
receive
|
|
125
|
+
receive : Callable
|
|
126
|
+
send : Callable
|
|
127
|
+
create_node : Optional[Callable]
|
|
128
|
+
delete_node : Optional[Callable]
|
|
129
|
+
get_run : Optional[Callable]
|
|
124
130
|
"""
|
|
125
131
|
log(
|
|
126
132
|
WARN,
|
|
@@ -145,6 +151,8 @@ def http_request_response( # pylint: disable=,R0913, R0914, R0915
|
|
|
145
151
|
"For the REST API, the root certificates "
|
|
146
152
|
"must be provided as a string path to the client.",
|
|
147
153
|
)
|
|
154
|
+
if authentication_keys is not None:
|
|
155
|
+
log(ERROR, "Client authentication is not supported for this transport type.")
|
|
148
156
|
|
|
149
157
|
# Shared variables for inner functions
|
|
150
158
|
metadata: Optional[Metadata] = None
|
flwr/client/supernode/app.py
CHANGED
|
@@ -29,6 +29,12 @@ from cryptography.hazmat.primitives.serialization import (
|
|
|
29
29
|
|
|
30
30
|
from flwr.client.client_app import ClientApp, LoadClientAppError
|
|
31
31
|
from flwr.common import EventType, event
|
|
32
|
+
from flwr.common.config import get_flwr_dir, get_project_config, get_project_dir
|
|
33
|
+
from flwr.common.constant import (
|
|
34
|
+
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
35
|
+
TRANSPORT_TYPE_GRPC_RERE,
|
|
36
|
+
TRANSPORT_TYPE_REST,
|
|
37
|
+
)
|
|
32
38
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
33
39
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
34
40
|
from flwr.common.object_ref import load_app, validate
|
|
@@ -44,11 +50,23 @@ def run_supernode() -> None:
|
|
|
44
50
|
|
|
45
51
|
event(EventType.RUN_SUPERNODE_ENTER)
|
|
46
52
|
|
|
47
|
-
|
|
53
|
+
args = _parse_args_run_supernode().parse_args()
|
|
54
|
+
|
|
55
|
+
_warn_deprecated_server_arg(args)
|
|
56
|
+
|
|
57
|
+
root_certificates = _get_certificates(args)
|
|
58
|
+
load_fn = _get_load_client_app_fn(args, multi_app=True)
|
|
59
|
+
authentication_keys = _try_setup_client_authentication(args)
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
_start_client_internal(
|
|
62
|
+
server_address=args.superlink,
|
|
63
|
+
load_client_app_fn=load_fn,
|
|
64
|
+
transport=args.transport,
|
|
65
|
+
root_certificates=root_certificates,
|
|
66
|
+
insecure=args.insecure,
|
|
67
|
+
authentication_keys=authentication_keys,
|
|
68
|
+
max_retries=args.max_retries,
|
|
69
|
+
max_wait_time=args.max_wait_time,
|
|
52
70
|
)
|
|
53
71
|
|
|
54
72
|
# Graceful shutdown
|
|
@@ -65,6 +83,27 @@ def run_client_app() -> None:
|
|
|
65
83
|
|
|
66
84
|
args = _parse_args_run_client_app().parse_args()
|
|
67
85
|
|
|
86
|
+
_warn_deprecated_server_arg(args)
|
|
87
|
+
|
|
88
|
+
root_certificates = _get_certificates(args)
|
|
89
|
+
load_fn = _get_load_client_app_fn(args, multi_app=False)
|
|
90
|
+
authentication_keys = _try_setup_client_authentication(args)
|
|
91
|
+
|
|
92
|
+
_start_client_internal(
|
|
93
|
+
server_address=args.superlink,
|
|
94
|
+
load_client_app_fn=load_fn,
|
|
95
|
+
transport=args.transport,
|
|
96
|
+
root_certificates=root_certificates,
|
|
97
|
+
insecure=args.insecure,
|
|
98
|
+
authentication_keys=authentication_keys,
|
|
99
|
+
max_retries=args.max_retries,
|
|
100
|
+
max_wait_time=args.max_wait_time,
|
|
101
|
+
)
|
|
102
|
+
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
|
|
106
|
+
"""Warn about the deprecated argument `--server`."""
|
|
68
107
|
if args.server != ADDRESS_FLEET_API_GRPC_RERE:
|
|
69
108
|
warn = "Passing flag --server is deprecated. Use --superlink instead."
|
|
70
109
|
warn_deprecated_feature(warn)
|
|
@@ -82,27 +121,6 @@ def run_client_app() -> None:
|
|
|
82
121
|
else:
|
|
83
122
|
args.superlink = args.server
|
|
84
123
|
|
|
85
|
-
root_certificates = _get_certificates(args)
|
|
86
|
-
log(
|
|
87
|
-
DEBUG,
|
|
88
|
-
"Flower will load ClientApp `%s`",
|
|
89
|
-
getattr(args, "client-app"),
|
|
90
|
-
)
|
|
91
|
-
load_fn = _get_load_client_app_fn(args)
|
|
92
|
-
authentication_keys = _try_setup_client_authentication(args)
|
|
93
|
-
|
|
94
|
-
_start_client_internal(
|
|
95
|
-
server_address=args.superlink,
|
|
96
|
-
load_client_app_fn=load_fn,
|
|
97
|
-
transport="rest" if args.rest else "grpc-rere",
|
|
98
|
-
root_certificates=root_certificates,
|
|
99
|
-
insecure=args.insecure,
|
|
100
|
-
authentication_keys=authentication_keys,
|
|
101
|
-
max_retries=args.max_retries,
|
|
102
|
-
max_wait_time=args.max_wait_time,
|
|
103
|
-
)
|
|
104
|
-
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
|
105
|
-
|
|
106
124
|
|
|
107
125
|
def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
108
126
|
"""Load certificates if specified in args."""
|
|
@@ -140,24 +158,88 @@ def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
|
140
158
|
|
|
141
159
|
|
|
142
160
|
def _get_load_client_app_fn(
|
|
143
|
-
args: argparse.Namespace,
|
|
144
|
-
) -> Callable[[], ClientApp]:
|
|
145
|
-
"""Get the load_client_app_fn function.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
161
|
+
args: argparse.Namespace, multi_app: bool
|
|
162
|
+
) -> Callable[[str, str], ClientApp]:
|
|
163
|
+
"""Get the load_client_app_fn function.
|
|
164
|
+
|
|
165
|
+
If `multi_app` is True, this function loads the specified ClientApp
|
|
166
|
+
based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
|
|
167
|
+
ClientApp will be loaded.
|
|
168
|
+
|
|
169
|
+
If `multi_app` is False, it ignores `fab_id` and `fab_version` and
|
|
170
|
+
loads a default ClientApp.
|
|
171
|
+
"""
|
|
172
|
+
# Find the Flower directory containing Flower Apps (only for multi-app)
|
|
173
|
+
flwr_dir = Path("")
|
|
174
|
+
if "flwr_dir" in args:
|
|
175
|
+
if args.flwr_dir is None:
|
|
176
|
+
flwr_dir = get_flwr_dir()
|
|
177
|
+
else:
|
|
178
|
+
flwr_dir = Path(args.flwr_dir).absolute()
|
|
149
179
|
|
|
150
|
-
|
|
151
|
-
valid, error_msg = validate(app_ref)
|
|
152
|
-
if not valid and error_msg:
|
|
153
|
-
raise LoadClientAppError(error_msg) from None
|
|
180
|
+
sys.path.insert(0, str(flwr_dir.absolute()))
|
|
154
181
|
|
|
155
|
-
|
|
156
|
-
|
|
182
|
+
default_app_ref: str = getattr(args, "client-app")
|
|
183
|
+
|
|
184
|
+
if not multi_app:
|
|
185
|
+
log(
|
|
186
|
+
DEBUG,
|
|
187
|
+
"Flower SuperNode will load and validate ClientApp `%s`",
|
|
188
|
+
getattr(args, "client-app"),
|
|
189
|
+
)
|
|
190
|
+
valid, error_msg = validate(default_app_ref)
|
|
191
|
+
if not valid and error_msg:
|
|
192
|
+
raise LoadClientAppError(error_msg) from None
|
|
193
|
+
|
|
194
|
+
def _load(fab_id: str, fab_version: str) -> ClientApp:
|
|
195
|
+
# If multi-app feature is disabled
|
|
196
|
+
if not multi_app:
|
|
197
|
+
# Get sys path to be inserted
|
|
198
|
+
sys_path = Path(args.dir).absolute()
|
|
199
|
+
|
|
200
|
+
# Set app reference
|
|
201
|
+
client_app_ref = default_app_ref
|
|
202
|
+
# If multi-app feature is enabled but the fab id is not specified
|
|
203
|
+
elif fab_id == "":
|
|
204
|
+
if default_app_ref == "":
|
|
205
|
+
raise LoadClientAppError(
|
|
206
|
+
"Invalid FAB ID: The FAB ID is empty.",
|
|
207
|
+
) from None
|
|
208
|
+
|
|
209
|
+
log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
|
|
210
|
+
# Get sys path to be inserted
|
|
211
|
+
sys_path = Path(args.dir).absolute()
|
|
212
|
+
|
|
213
|
+
# Set app reference
|
|
214
|
+
client_app_ref = default_app_ref
|
|
215
|
+
# If multi-app feature is enabled
|
|
216
|
+
else:
|
|
217
|
+
try:
|
|
218
|
+
project_dir = get_project_dir(fab_id, fab_version, flwr_dir)
|
|
219
|
+
config = get_project_config(project_dir)
|
|
220
|
+
except Exception as e:
|
|
221
|
+
raise LoadClientAppError("Failed to load ClientApp") from e
|
|
222
|
+
|
|
223
|
+
# Get sys path to be inserted
|
|
224
|
+
sys_path = Path(project_dir).absolute()
|
|
225
|
+
|
|
226
|
+
# Set app reference
|
|
227
|
+
client_app_ref = config["flower"]["components"]["clientapp"]
|
|
228
|
+
|
|
229
|
+
# Set sys.path
|
|
230
|
+
sys.path.insert(0, str(sys_path))
|
|
231
|
+
|
|
232
|
+
# Load ClientApp
|
|
233
|
+
log(
|
|
234
|
+
DEBUG,
|
|
235
|
+
"Loading ClientApp `%s`",
|
|
236
|
+
client_app_ref,
|
|
237
|
+
)
|
|
238
|
+
client_app = load_app(client_app_ref, LoadClientAppError, sys_path)
|
|
157
239
|
|
|
158
240
|
if not isinstance(client_app, ClientApp):
|
|
159
241
|
raise LoadClientAppError(
|
|
160
|
-
f"Attribute {
|
|
242
|
+
f"Attribute {client_app_ref} is not of type {ClientApp}",
|
|
161
243
|
) from None
|
|
162
244
|
|
|
163
245
|
return client_app
|
|
@@ -185,7 +267,7 @@ def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
|
185
267
|
"--flwr-dir",
|
|
186
268
|
default=None,
|
|
187
269
|
help="""The path containing installed Flower Apps.
|
|
188
|
-
By default, this value
|
|
270
|
+
By default, this value is equal to:
|
|
189
271
|
|
|
190
272
|
- `$FLWR_HOME/` if `$FLWR_HOME` is defined
|
|
191
273
|
- `$XDG_DATA_HOME/.flwr/` if `$XDG_DATA_HOME` is defined
|
|
@@ -218,9 +300,27 @@ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
|
|
|
218
300
|
help="Run the client without HTTPS. By default, the client runs with "
|
|
219
301
|
"HTTPS enabled. Use this flag only if you understand the risks.",
|
|
220
302
|
)
|
|
221
|
-
parser.
|
|
303
|
+
ex_group = parser.add_mutually_exclusive_group()
|
|
304
|
+
ex_group.add_argument(
|
|
305
|
+
"--grpc-rere",
|
|
306
|
+
action="store_const",
|
|
307
|
+
dest="transport",
|
|
308
|
+
const=TRANSPORT_TYPE_GRPC_RERE,
|
|
309
|
+
default=TRANSPORT_TYPE_GRPC_RERE,
|
|
310
|
+
help="Use grpc-rere as a transport layer for the client.",
|
|
311
|
+
)
|
|
312
|
+
ex_group.add_argument(
|
|
313
|
+
"--grpc-adapter",
|
|
314
|
+
action="store_const",
|
|
315
|
+
dest="transport",
|
|
316
|
+
const=TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
317
|
+
help="Use grpc-adapter as a transport layer for the client.",
|
|
318
|
+
)
|
|
319
|
+
ex_group.add_argument(
|
|
222
320
|
"--rest",
|
|
223
|
-
action="
|
|
321
|
+
action="store_const",
|
|
322
|
+
dest="transport",
|
|
323
|
+
const=TRANSPORT_TYPE_REST,
|
|
224
324
|
help="Use REST as a transport layer for the client.",
|
|
225
325
|
)
|
|
226
326
|
parser.add_argument(
|