flwr-nightly 1.11.0.dev20240815__py3-none-any.whl → 1.11.0.dev20240817__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/config_utils.py +2 -2
- flwr/cli/run/run.py +5 -4
- flwr/client/app.py +97 -11
- flwr/client/{process → clientapp}/__init__.py +7 -0
- flwr/client/clientapp/app.py +178 -0
- flwr/client/clientapp/clientappio_servicer.py +238 -0
- flwr/client/clientapp/utils.py +108 -0
- flwr/client/grpc_rere_client/connection.py +8 -1
- flwr/client/rest_client/connection.py +14 -2
- flwr/client/supernode/__init__.py +0 -2
- flwr/client/supernode/app.py +21 -120
- flwr/proto/clientappio_pb2.py +17 -13
- flwr/proto/clientappio_pb2.pyi +17 -0
- flwr/proto/clientappio_pb2_grpc.py +34 -0
- flwr/proto/clientappio_pb2_grpc.pyi +13 -0
- flwr/server/app.py +3 -0
- flwr/server/run_serverapp.py +18 -2
- flwr/server/server.py +3 -1
- flwr/server/superlink/driver/driver_servicer.py +23 -8
- flwr/server/superlink/ffs/disk_ffs.py +6 -3
- flwr/server/superlink/ffs/ffs.py +3 -3
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +9 -3
- flwr/server/superlink/fleet/message_handler/message_handler.py +16 -1
- flwr/server/superlink/fleet/vce/vce_api.py +2 -2
- flwr/server/workflow/default_workflows.py +3 -1
- flwr/superexec/deployment.py +8 -9
- {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/METADATA +1 -1
- {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/RECORD +31 -29
- {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/entry_points.txt +1 -1
- flwr/client/process/clientappio_servicer.py +0 -145
- {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.11.0.dev20240815.dist-info → flwr_nightly-1.11.0.dev20240817.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
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
|
+
"""Flower ClientApp loading utils."""
|
|
16
|
+
|
|
17
|
+
from logging import DEBUG
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Callable, Optional
|
|
20
|
+
|
|
21
|
+
from flwr.client.client_app import ClientApp, LoadClientAppError
|
|
22
|
+
from flwr.common.config import (
|
|
23
|
+
get_flwr_dir,
|
|
24
|
+
get_metadata_from_config,
|
|
25
|
+
get_project_config,
|
|
26
|
+
get_project_dir,
|
|
27
|
+
)
|
|
28
|
+
from flwr.common.logger import log
|
|
29
|
+
from flwr.common.object_ref import load_app, validate
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_load_client_app_fn(
|
|
33
|
+
default_app_ref: str,
|
|
34
|
+
app_path: Optional[str],
|
|
35
|
+
multi_app: bool,
|
|
36
|
+
flwr_dir: Optional[str] = None,
|
|
37
|
+
) -> Callable[[str, str], ClientApp]:
|
|
38
|
+
"""Get the load_client_app_fn function.
|
|
39
|
+
|
|
40
|
+
If `multi_app` is True, this function loads the specified ClientApp
|
|
41
|
+
based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
|
|
42
|
+
ClientApp will be loaded.
|
|
43
|
+
|
|
44
|
+
If `multi_app` is False, it ignores `fab_id` and `fab_version` and
|
|
45
|
+
loads a default ClientApp.
|
|
46
|
+
"""
|
|
47
|
+
if not multi_app:
|
|
48
|
+
log(
|
|
49
|
+
DEBUG,
|
|
50
|
+
"Flower SuperNode will load and validate ClientApp `%s`",
|
|
51
|
+
default_app_ref,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
valid, error_msg = validate(default_app_ref, project_dir=app_path)
|
|
55
|
+
if not valid and error_msg:
|
|
56
|
+
raise LoadClientAppError(error_msg) from None
|
|
57
|
+
|
|
58
|
+
def _load(fab_id: str, fab_version: str) -> ClientApp:
|
|
59
|
+
runtime_app_dir = Path(app_path if app_path else "").absolute()
|
|
60
|
+
# If multi-app feature is disabled
|
|
61
|
+
if not multi_app:
|
|
62
|
+
# Set app reference
|
|
63
|
+
client_app_ref = default_app_ref
|
|
64
|
+
# If multi-app feature is enabled but app directory is provided
|
|
65
|
+
elif app_path is not None:
|
|
66
|
+
config = get_project_config(runtime_app_dir)
|
|
67
|
+
this_fab_version, this_fab_id = get_metadata_from_config(config)
|
|
68
|
+
|
|
69
|
+
if this_fab_version != fab_version or this_fab_id != fab_id:
|
|
70
|
+
raise LoadClientAppError(
|
|
71
|
+
f"FAB ID or version mismatch: Expected FAB ID '{this_fab_id}' and "
|
|
72
|
+
f"FAB version '{this_fab_version}', but received FAB ID '{fab_id}' "
|
|
73
|
+
f"and FAB version '{fab_version}'.",
|
|
74
|
+
) from None
|
|
75
|
+
|
|
76
|
+
# log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
|
|
77
|
+
|
|
78
|
+
# Set app reference
|
|
79
|
+
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
80
|
+
# If multi-app feature is enabled
|
|
81
|
+
else:
|
|
82
|
+
try:
|
|
83
|
+
runtime_app_dir = get_project_dir(
|
|
84
|
+
fab_id, fab_version, get_flwr_dir(flwr_dir)
|
|
85
|
+
)
|
|
86
|
+
config = get_project_config(runtime_app_dir)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
raise LoadClientAppError("Failed to load ClientApp") from e
|
|
89
|
+
|
|
90
|
+
# Set app reference
|
|
91
|
+
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
92
|
+
|
|
93
|
+
# Load ClientApp
|
|
94
|
+
log(
|
|
95
|
+
DEBUG,
|
|
96
|
+
"Loading ClientApp `%s`",
|
|
97
|
+
client_app_ref,
|
|
98
|
+
)
|
|
99
|
+
client_app = load_app(client_app_ref, LoadClientAppError, runtime_app_dir)
|
|
100
|
+
|
|
101
|
+
if not isinstance(client_app, ClientApp):
|
|
102
|
+
raise LoadClientAppError(
|
|
103
|
+
f"Attribute {client_app_ref} is not of type {ClientApp}",
|
|
104
|
+
) from None
|
|
105
|
+
|
|
106
|
+
return client_app
|
|
107
|
+
|
|
108
|
+
return _load
|
|
@@ -46,6 +46,7 @@ from flwr.common.serde import (
|
|
|
46
46
|
user_config_from_proto,
|
|
47
47
|
)
|
|
48
48
|
from flwr.common.typing import Fab, Run
|
|
49
|
+
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
49
50
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
50
51
|
CreateNodeRequest,
|
|
51
52
|
DeleteNodeRequest,
|
|
@@ -292,7 +293,13 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
|
|
|
292
293
|
|
|
293
294
|
def get_fab(fab_hash: str) -> Fab:
|
|
294
295
|
# Call FleetAPI
|
|
295
|
-
|
|
296
|
+
get_fab_request = GetFabRequest(hash_str=fab_hash)
|
|
297
|
+
get_fab_response: GetFabResponse = retry_invoker.invoke(
|
|
298
|
+
stub.GetFab,
|
|
299
|
+
request=get_fab_request,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
return Fab(get_fab_response.fab.hash_str, get_fab_response.fab.content)
|
|
296
303
|
|
|
297
304
|
try:
|
|
298
305
|
# Yield methods
|
|
@@ -46,6 +46,7 @@ from flwr.common.serde import (
|
|
|
46
46
|
user_config_from_proto,
|
|
47
47
|
)
|
|
48
48
|
from flwr.common.typing import Fab, Run
|
|
49
|
+
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
49
50
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
50
51
|
CreateNodeRequest,
|
|
51
52
|
CreateNodeResponse,
|
|
@@ -74,6 +75,7 @@ PATH_PULL_TASK_INS: str = "api/v0/fleet/pull-task-ins"
|
|
|
74
75
|
PATH_PUSH_TASK_RES: str = "api/v0/fleet/push-task-res"
|
|
75
76
|
PATH_PING: str = "api/v0/fleet/ping"
|
|
76
77
|
PATH_GET_RUN: str = "/api/v0/fleet/get-run"
|
|
78
|
+
PATH_GET_FAB: str = "/api/v0/fleet/get-fab"
|
|
77
79
|
|
|
78
80
|
T = TypeVar("T", bound=GrpcMessage)
|
|
79
81
|
|
|
@@ -369,8 +371,18 @@ def http_request_response( # pylint: disable=,R0913, R0914, R0915
|
|
|
369
371
|
)
|
|
370
372
|
|
|
371
373
|
def get_fab(fab_hash: str) -> Fab:
|
|
372
|
-
#
|
|
373
|
-
|
|
374
|
+
# Construct the request
|
|
375
|
+
req = GetFabRequest(hash_str=fab_hash)
|
|
376
|
+
|
|
377
|
+
# Send the request
|
|
378
|
+
res = _request(req, GetFabResponse, PATH_GET_FAB)
|
|
379
|
+
if res is None:
|
|
380
|
+
return Fab("", b"")
|
|
381
|
+
|
|
382
|
+
return Fab(
|
|
383
|
+
res.fab.hash_str,
|
|
384
|
+
res.fab.content,
|
|
385
|
+
)
|
|
374
386
|
|
|
375
387
|
try:
|
|
376
388
|
# Yield methods
|
|
@@ -15,12 +15,10 @@
|
|
|
15
15
|
"""Flower SuperNode."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
from .app import flwr_clientapp as flwr_clientapp
|
|
19
18
|
from .app import run_client_app as run_client_app
|
|
20
19
|
from .app import run_supernode as run_supernode
|
|
21
20
|
|
|
22
21
|
__all__ = [
|
|
23
|
-
"flwr_clientapp",
|
|
24
22
|
"run_client_app",
|
|
25
23
|
"run_supernode",
|
|
26
24
|
]
|
flwr/client/supernode/app.py
CHANGED
|
@@ -18,7 +18,7 @@ import argparse
|
|
|
18
18
|
import sys
|
|
19
19
|
from logging import DEBUG, INFO, WARN
|
|
20
20
|
from pathlib import Path
|
|
21
|
-
from typing import
|
|
21
|
+
from typing import Optional, Tuple
|
|
22
22
|
|
|
23
23
|
from cryptography.exceptions import UnsupportedAlgorithm
|
|
24
24
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
@@ -27,15 +27,8 @@ from cryptography.hazmat.primitives.serialization import (
|
|
|
27
27
|
load_ssh_public_key,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
from flwr.client.client_app import ClientApp, LoadClientAppError
|
|
31
30
|
from flwr.common import EventType, event
|
|
32
|
-
from flwr.common.config import
|
|
33
|
-
get_flwr_dir,
|
|
34
|
-
get_metadata_from_config,
|
|
35
|
-
get_project_config,
|
|
36
|
-
get_project_dir,
|
|
37
|
-
parse_config_args,
|
|
38
|
-
)
|
|
31
|
+
from flwr.common.config import parse_config_args
|
|
39
32
|
from flwr.common.constant import (
|
|
40
33
|
TRANSPORT_TYPE_GRPC_ADAPTER,
|
|
41
34
|
TRANSPORT_TYPE_GRPC_RERE,
|
|
@@ -43,9 +36,9 @@ from flwr.common.constant import (
|
|
|
43
36
|
)
|
|
44
37
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
45
38
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
46
|
-
from flwr.common.object_ref import load_app, validate
|
|
47
39
|
|
|
48
|
-
from ..app import
|
|
40
|
+
from ..app import start_client_internal
|
|
41
|
+
from ..clientapp.utils import get_load_client_app_fn
|
|
49
42
|
|
|
50
43
|
ADDRESS_FLEET_API_GRPC_RERE = "0.0.0.0:9092"
|
|
51
44
|
|
|
@@ -61,7 +54,7 @@ def run_supernode() -> None:
|
|
|
61
54
|
_warn_deprecated_server_arg(args)
|
|
62
55
|
|
|
63
56
|
root_certificates = _get_certificates(args)
|
|
64
|
-
load_fn =
|
|
57
|
+
load_fn = get_load_client_app_fn(
|
|
65
58
|
default_app_ref="",
|
|
66
59
|
app_path=args.app,
|
|
67
60
|
flwr_dir=args.flwr_dir,
|
|
@@ -69,7 +62,7 @@ def run_supernode() -> None:
|
|
|
69
62
|
)
|
|
70
63
|
authentication_keys = _try_setup_client_authentication(args)
|
|
71
64
|
|
|
72
|
-
|
|
65
|
+
start_client_internal(
|
|
73
66
|
server_address=args.superlink,
|
|
74
67
|
load_client_app_fn=load_fn,
|
|
75
68
|
transport=args.transport,
|
|
@@ -79,7 +72,8 @@ def run_supernode() -> None:
|
|
|
79
72
|
max_retries=args.max_retries,
|
|
80
73
|
max_wait_time=args.max_wait_time,
|
|
81
74
|
node_config=parse_config_args([args.node_config]),
|
|
82
|
-
|
|
75
|
+
isolate=args.isolate,
|
|
76
|
+
supernode_address=args.supernode_address,
|
|
83
77
|
)
|
|
84
78
|
|
|
85
79
|
# Graceful shutdown
|
|
@@ -99,14 +93,14 @@ def run_client_app() -> None:
|
|
|
99
93
|
_warn_deprecated_server_arg(args)
|
|
100
94
|
|
|
101
95
|
root_certificates = _get_certificates(args)
|
|
102
|
-
load_fn =
|
|
96
|
+
load_fn = get_load_client_app_fn(
|
|
103
97
|
default_app_ref=getattr(args, "client-app"),
|
|
104
98
|
app_path=args.dir,
|
|
105
99
|
multi_app=False,
|
|
106
100
|
)
|
|
107
101
|
authentication_keys = _try_setup_client_authentication(args)
|
|
108
102
|
|
|
109
|
-
|
|
103
|
+
start_client_internal(
|
|
110
104
|
server_address=args.superlink,
|
|
111
105
|
node_config=parse_config_args([args.node_config]),
|
|
112
106
|
load_client_app_fn=load_fn,
|
|
@@ -120,31 +114,6 @@ def run_client_app() -> None:
|
|
|
120
114
|
register_exit_handlers(event_type=EventType.RUN_CLIENT_APP_LEAVE)
|
|
121
115
|
|
|
122
116
|
|
|
123
|
-
def flwr_clientapp() -> None:
|
|
124
|
-
"""Run process-isolated Flower ClientApp."""
|
|
125
|
-
log(INFO, "Starting Flower ClientApp")
|
|
126
|
-
|
|
127
|
-
parser = argparse.ArgumentParser(
|
|
128
|
-
description="Run a Flower ClientApp",
|
|
129
|
-
)
|
|
130
|
-
parser.add_argument(
|
|
131
|
-
"--address",
|
|
132
|
-
help="Address of SuperNode ClientAppIo gRPC servicer",
|
|
133
|
-
)
|
|
134
|
-
parser.add_argument(
|
|
135
|
-
"--token",
|
|
136
|
-
help="Unique token generated by SuperNode for each ClientApp execution",
|
|
137
|
-
)
|
|
138
|
-
args = parser.parse_args()
|
|
139
|
-
log(
|
|
140
|
-
DEBUG,
|
|
141
|
-
"Staring isolated `ClientApp` connected to SuperNode ClientAppIo at %s "
|
|
142
|
-
"with the token %s",
|
|
143
|
-
args.address,
|
|
144
|
-
args.token,
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
|
|
148
117
|
def _warn_deprecated_server_arg(args: argparse.Namespace) -> None:
|
|
149
118
|
"""Warn about the deprecated argument `--server`."""
|
|
150
119
|
if args.server != ADDRESS_FLEET_API_GRPC_RERE:
|
|
@@ -200,85 +169,6 @@ def _get_certificates(args: argparse.Namespace) -> Optional[bytes]:
|
|
|
200
169
|
return root_certificates
|
|
201
170
|
|
|
202
171
|
|
|
203
|
-
def _get_load_client_app_fn(
|
|
204
|
-
default_app_ref: str,
|
|
205
|
-
app_path: Optional[str],
|
|
206
|
-
multi_app: bool,
|
|
207
|
-
flwr_dir: Optional[str] = None,
|
|
208
|
-
) -> Callable[[str, str], ClientApp]:
|
|
209
|
-
"""Get the load_client_app_fn function.
|
|
210
|
-
|
|
211
|
-
If `multi_app` is True, this function loads the specified ClientApp
|
|
212
|
-
based on `fab_id` and `fab_version`. If `fab_id` is empty, a default
|
|
213
|
-
ClientApp will be loaded.
|
|
214
|
-
|
|
215
|
-
If `multi_app` is False, it ignores `fab_id` and `fab_version` and
|
|
216
|
-
loads a default ClientApp.
|
|
217
|
-
"""
|
|
218
|
-
if not multi_app:
|
|
219
|
-
log(
|
|
220
|
-
DEBUG,
|
|
221
|
-
"Flower SuperNode will load and validate ClientApp `%s`",
|
|
222
|
-
default_app_ref,
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
valid, error_msg = validate(default_app_ref, project_dir=app_path)
|
|
226
|
-
if not valid and error_msg:
|
|
227
|
-
raise LoadClientAppError(error_msg) from None
|
|
228
|
-
|
|
229
|
-
def _load(fab_id: str, fab_version: str) -> ClientApp:
|
|
230
|
-
runtime_app_dir = Path(app_path if app_path else "").absolute()
|
|
231
|
-
# If multi-app feature is disabled
|
|
232
|
-
if not multi_app:
|
|
233
|
-
# Set app reference
|
|
234
|
-
client_app_ref = default_app_ref
|
|
235
|
-
# If multi-app feature is enabled but app directory is provided
|
|
236
|
-
elif app_path is not None:
|
|
237
|
-
config = get_project_config(runtime_app_dir)
|
|
238
|
-
this_fab_version, this_fab_id = get_metadata_from_config(config)
|
|
239
|
-
|
|
240
|
-
if this_fab_version != fab_version or this_fab_id != fab_id:
|
|
241
|
-
raise LoadClientAppError(
|
|
242
|
-
f"FAB ID or version mismatch: Expected FAB ID '{this_fab_id}' and "
|
|
243
|
-
f"FAB version '{this_fab_version}', but received FAB ID '{fab_id}' "
|
|
244
|
-
f"and FAB version '{fab_version}'.",
|
|
245
|
-
) from None
|
|
246
|
-
|
|
247
|
-
# log(WARN, "FAB ID is not provided; the default ClientApp will be loaded.")
|
|
248
|
-
|
|
249
|
-
# Set app reference
|
|
250
|
-
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
251
|
-
# If multi-app feature is enabled
|
|
252
|
-
else:
|
|
253
|
-
try:
|
|
254
|
-
runtime_app_dir = get_project_dir(
|
|
255
|
-
fab_id, fab_version, get_flwr_dir(flwr_dir)
|
|
256
|
-
)
|
|
257
|
-
config = get_project_config(runtime_app_dir)
|
|
258
|
-
except Exception as e:
|
|
259
|
-
raise LoadClientAppError("Failed to load ClientApp") from e
|
|
260
|
-
|
|
261
|
-
# Set app reference
|
|
262
|
-
client_app_ref = config["tool"]["flwr"]["app"]["components"]["clientapp"]
|
|
263
|
-
|
|
264
|
-
# Load ClientApp
|
|
265
|
-
log(
|
|
266
|
-
DEBUG,
|
|
267
|
-
"Loading ClientApp `%s`",
|
|
268
|
-
client_app_ref,
|
|
269
|
-
)
|
|
270
|
-
client_app = load_app(client_app_ref, LoadClientAppError, runtime_app_dir)
|
|
271
|
-
|
|
272
|
-
if not isinstance(client_app, ClientApp):
|
|
273
|
-
raise LoadClientAppError(
|
|
274
|
-
f"Attribute {client_app_ref} is not of type {ClientApp}",
|
|
275
|
-
) from None
|
|
276
|
-
|
|
277
|
-
return client_app
|
|
278
|
-
|
|
279
|
-
return _load
|
|
280
|
-
|
|
281
|
-
|
|
282
172
|
def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
283
173
|
"""Parse flower-supernode command line arguments."""
|
|
284
174
|
parser = argparse.ArgumentParser(
|
|
@@ -308,6 +198,17 @@ def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
|
308
198
|
- `$HOME/.flwr/` in all other cases
|
|
309
199
|
""",
|
|
310
200
|
)
|
|
201
|
+
parser.add_argument(
|
|
202
|
+
"--isolate",
|
|
203
|
+
action="store_true",
|
|
204
|
+
help="Run `ClientApp` in an isolated subprocess. By default, `ClientApp` "
|
|
205
|
+
"runs in the same process that executes the SuperNode.",
|
|
206
|
+
)
|
|
207
|
+
parser.add_argument(
|
|
208
|
+
"--supernode-address",
|
|
209
|
+
default="0.0.0.0:9094",
|
|
210
|
+
help="Set the SuperNode gRPC server address. Defaults to `0.0.0.0:9094`.",
|
|
211
|
+
)
|
|
311
212
|
|
|
312
213
|
return parser
|
|
313
214
|
|
flwr/proto/clientappio_pb2.py
CHANGED
|
@@ -17,25 +17,29 @@ from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
|
|
|
17
17
|
from flwr.proto import message_pb2 as flwr_dot_proto_dot_message__pb2
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/clientappio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x18\x66lwr/proto/message.proto\"W\n\x15\x43lientAppOutputStatus\x12-\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x1f.flwr.proto.ClientAppOutputCode\x12\x0f\n\x07message\x18\x02 \x01(\t\"+\n\x1aPullClientAppInputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\"\x87\x01\n\x1bPullClientAppInputsResponse\x12$\n\x07message\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Run\"x\n\x1bPushClientAppOutputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\x12$\n\x07message\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x03 \x01(\x0b\x32\x13.flwr.proto.Context\"Q\n\x1cPushClientAppOutputsResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.flwr.proto.ClientAppOutputStatus*L\n\x13\x43lientAppOutputCode\x12\x0b\n\x07SUCCESS\x10\x00\x12\x15\n\x11\x44\x45\x41\x44LINE_EXCEEDED\x10\x01\x12\x11\n\rUNKNOWN_ERROR\x10\x02\x32\
|
|
20
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1c\x66lwr/proto/clientappio.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x18\x66lwr/proto/message.proto\"W\n\x15\x43lientAppOutputStatus\x12-\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x1f.flwr.proto.ClientAppOutputCode\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x11\n\x0fGetTokenRequest\"!\n\x10GetTokenResponse\x12\r\n\x05token\x18\x01 \x01(\x12\"+\n\x1aPullClientAppInputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\"\x87\x01\n\x1bPullClientAppInputsResponse\x12$\n\x07message\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Run\"x\n\x1bPushClientAppOutputsRequest\x12\r\n\x05token\x18\x01 \x01(\x12\x12$\n\x07message\x18\x02 \x01(\x0b\x32\x13.flwr.proto.Message\x12$\n\x07\x63ontext\x18\x03 \x01(\x0b\x32\x13.flwr.proto.Context\"Q\n\x1cPushClientAppOutputsResponse\x12\x31\n\x06status\x18\x01 \x01(\x0b\x32!.flwr.proto.ClientAppOutputStatus*L\n\x13\x43lientAppOutputCode\x12\x0b\n\x07SUCCESS\x10\x00\x12\x15\n\x11\x44\x45\x41\x44LINE_EXCEEDED\x10\x01\x12\x11\n\rUNKNOWN_ERROR\x10\x02\x32\xad\x02\n\x0b\x43lientAppIo\x12G\n\x08GetToken\x12\x1b.flwr.proto.GetTokenRequest\x1a\x1c.flwr.proto.GetTokenResponse\"\x00\x12h\n\x13PullClientAppInputs\x12&.flwr.proto.PullClientAppInputsRequest\x1a\'.flwr.proto.PullClientAppInputsResponse\"\x00\x12k\n\x14PushClientAppOutputs\x12\'.flwr.proto.PushClientAppOutputsRequest\x1a(.flwr.proto.PushClientAppOutputsResponse\"\x00\x62\x06proto3')
|
|
21
21
|
|
|
22
22
|
_globals = globals()
|
|
23
23
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
24
24
|
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.clientappio_pb2', _globals)
|
|
25
25
|
if _descriptor._USE_C_DESCRIPTORS == False:
|
|
26
26
|
DESCRIPTOR._options = None
|
|
27
|
-
_globals['_CLIENTAPPOUTPUTCODE']._serialized_start=
|
|
28
|
-
_globals['_CLIENTAPPOUTPUTCODE']._serialized_end=
|
|
27
|
+
_globals['_CLIENTAPPOUTPUTCODE']._serialized_start=645
|
|
28
|
+
_globals['_CLIENTAPPOUTPUTCODE']._serialized_end=721
|
|
29
29
|
_globals['_CLIENTAPPOUTPUTSTATUS']._serialized_start=114
|
|
30
30
|
_globals['_CLIENTAPPOUTPUTSTATUS']._serialized_end=201
|
|
31
|
-
_globals['
|
|
32
|
-
_globals['
|
|
33
|
-
_globals['
|
|
34
|
-
_globals['
|
|
35
|
-
_globals['
|
|
36
|
-
_globals['
|
|
37
|
-
_globals['
|
|
38
|
-
_globals['
|
|
39
|
-
_globals['
|
|
40
|
-
_globals['
|
|
31
|
+
_globals['_GETTOKENREQUEST']._serialized_start=203
|
|
32
|
+
_globals['_GETTOKENREQUEST']._serialized_end=220
|
|
33
|
+
_globals['_GETTOKENRESPONSE']._serialized_start=222
|
|
34
|
+
_globals['_GETTOKENRESPONSE']._serialized_end=255
|
|
35
|
+
_globals['_PULLCLIENTAPPINPUTSREQUEST']._serialized_start=257
|
|
36
|
+
_globals['_PULLCLIENTAPPINPUTSREQUEST']._serialized_end=300
|
|
37
|
+
_globals['_PULLCLIENTAPPINPUTSRESPONSE']._serialized_start=303
|
|
38
|
+
_globals['_PULLCLIENTAPPINPUTSRESPONSE']._serialized_end=438
|
|
39
|
+
_globals['_PUSHCLIENTAPPOUTPUTSREQUEST']._serialized_start=440
|
|
40
|
+
_globals['_PUSHCLIENTAPPOUTPUTSREQUEST']._serialized_end=560
|
|
41
|
+
_globals['_PUSHCLIENTAPPOUTPUTSRESPONSE']._serialized_start=562
|
|
42
|
+
_globals['_PUSHCLIENTAPPOUTPUTSRESPONSE']._serialized_end=643
|
|
43
|
+
_globals['_CLIENTAPPIO']._serialized_start=724
|
|
44
|
+
_globals['_CLIENTAPPIO']._serialized_end=1025
|
|
41
45
|
# @@protoc_insertion_point(module_scope)
|
flwr/proto/clientappio_pb2.pyi
CHANGED
|
@@ -44,6 +44,23 @@ class ClientAppOutputStatus(google.protobuf.message.Message):
|
|
|
44
44
|
def ClearField(self, field_name: typing_extensions.Literal["code",b"code","message",b"message"]) -> None: ...
|
|
45
45
|
global___ClientAppOutputStatus = ClientAppOutputStatus
|
|
46
46
|
|
|
47
|
+
class GetTokenRequest(google.protobuf.message.Message):
|
|
48
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
49
|
+
def __init__(self,
|
|
50
|
+
) -> None: ...
|
|
51
|
+
global___GetTokenRequest = GetTokenRequest
|
|
52
|
+
|
|
53
|
+
class GetTokenResponse(google.protobuf.message.Message):
|
|
54
|
+
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
55
|
+
TOKEN_FIELD_NUMBER: builtins.int
|
|
56
|
+
token: builtins.int
|
|
57
|
+
def __init__(self,
|
|
58
|
+
*,
|
|
59
|
+
token: builtins.int = ...,
|
|
60
|
+
) -> None: ...
|
|
61
|
+
def ClearField(self, field_name: typing_extensions.Literal["token",b"token"]) -> None: ...
|
|
62
|
+
global___GetTokenResponse = GetTokenResponse
|
|
63
|
+
|
|
47
64
|
class PullClientAppInputsRequest(google.protobuf.message.Message):
|
|
48
65
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
|
49
66
|
TOKEN_FIELD_NUMBER: builtins.int
|
|
@@ -14,6 +14,11 @@ class ClientAppIoStub(object):
|
|
|
14
14
|
Args:
|
|
15
15
|
channel: A grpc.Channel.
|
|
16
16
|
"""
|
|
17
|
+
self.GetToken = channel.unary_unary(
|
|
18
|
+
'/flwr.proto.ClientAppIo/GetToken',
|
|
19
|
+
request_serializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenRequest.SerializeToString,
|
|
20
|
+
response_deserializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenResponse.FromString,
|
|
21
|
+
)
|
|
17
22
|
self.PullClientAppInputs = channel.unary_unary(
|
|
18
23
|
'/flwr.proto.ClientAppIo/PullClientAppInputs',
|
|
19
24
|
request_serializer=flwr_dot_proto_dot_clientappio__pb2.PullClientAppInputsRequest.SerializeToString,
|
|
@@ -29,6 +34,13 @@ class ClientAppIoStub(object):
|
|
|
29
34
|
class ClientAppIoServicer(object):
|
|
30
35
|
"""Missing associated documentation comment in .proto file."""
|
|
31
36
|
|
|
37
|
+
def GetToken(self, request, context):
|
|
38
|
+
"""Get token
|
|
39
|
+
"""
|
|
40
|
+
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
|
41
|
+
context.set_details('Method not implemented!')
|
|
42
|
+
raise NotImplementedError('Method not implemented!')
|
|
43
|
+
|
|
32
44
|
def PullClientAppInputs(self, request, context):
|
|
33
45
|
"""Get Message, Context, and Run
|
|
34
46
|
"""
|
|
@@ -46,6 +58,11 @@ class ClientAppIoServicer(object):
|
|
|
46
58
|
|
|
47
59
|
def add_ClientAppIoServicer_to_server(servicer, server):
|
|
48
60
|
rpc_method_handlers = {
|
|
61
|
+
'GetToken': grpc.unary_unary_rpc_method_handler(
|
|
62
|
+
servicer.GetToken,
|
|
63
|
+
request_deserializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenRequest.FromString,
|
|
64
|
+
response_serializer=flwr_dot_proto_dot_clientappio__pb2.GetTokenResponse.SerializeToString,
|
|
65
|
+
),
|
|
49
66
|
'PullClientAppInputs': grpc.unary_unary_rpc_method_handler(
|
|
50
67
|
servicer.PullClientAppInputs,
|
|
51
68
|
request_deserializer=flwr_dot_proto_dot_clientappio__pb2.PullClientAppInputsRequest.FromString,
|
|
@@ -66,6 +83,23 @@ def add_ClientAppIoServicer_to_server(servicer, server):
|
|
|
66
83
|
class ClientAppIo(object):
|
|
67
84
|
"""Missing associated documentation comment in .proto file."""
|
|
68
85
|
|
|
86
|
+
@staticmethod
|
|
87
|
+
def GetToken(request,
|
|
88
|
+
target,
|
|
89
|
+
options=(),
|
|
90
|
+
channel_credentials=None,
|
|
91
|
+
call_credentials=None,
|
|
92
|
+
insecure=False,
|
|
93
|
+
compression=None,
|
|
94
|
+
wait_for_ready=None,
|
|
95
|
+
timeout=None,
|
|
96
|
+
metadata=None):
|
|
97
|
+
return grpc.experimental.unary_unary(request, target, '/flwr.proto.ClientAppIo/GetToken',
|
|
98
|
+
flwr_dot_proto_dot_clientappio__pb2.GetTokenRequest.SerializeToString,
|
|
99
|
+
flwr_dot_proto_dot_clientappio__pb2.GetTokenResponse.FromString,
|
|
100
|
+
options, channel_credentials,
|
|
101
|
+
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
|
|
102
|
+
|
|
69
103
|
@staticmethod
|
|
70
104
|
def PullClientAppInputs(request,
|
|
71
105
|
target,
|
|
@@ -8,6 +8,11 @@ import grpc
|
|
|
8
8
|
|
|
9
9
|
class ClientAppIoStub:
|
|
10
10
|
def __init__(self, channel: grpc.Channel) -> None: ...
|
|
11
|
+
GetToken: grpc.UnaryUnaryMultiCallable[
|
|
12
|
+
flwr.proto.clientappio_pb2.GetTokenRequest,
|
|
13
|
+
flwr.proto.clientappio_pb2.GetTokenResponse]
|
|
14
|
+
"""Get token"""
|
|
15
|
+
|
|
11
16
|
PullClientAppInputs: grpc.UnaryUnaryMultiCallable[
|
|
12
17
|
flwr.proto.clientappio_pb2.PullClientAppInputsRequest,
|
|
13
18
|
flwr.proto.clientappio_pb2.PullClientAppInputsResponse]
|
|
@@ -20,6 +25,14 @@ class ClientAppIoStub:
|
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
class ClientAppIoServicer(metaclass=abc.ABCMeta):
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def GetToken(self,
|
|
30
|
+
request: flwr.proto.clientappio_pb2.GetTokenRequest,
|
|
31
|
+
context: grpc.ServicerContext,
|
|
32
|
+
) -> flwr.proto.clientappio_pb2.GetTokenResponse:
|
|
33
|
+
"""Get token"""
|
|
34
|
+
pass
|
|
35
|
+
|
|
23
36
|
@abc.abstractmethod
|
|
24
37
|
def PullClientAppInputs(self,
|
|
25
38
|
request: flwr.proto.clientappio_pb2.PullClientAppInputsRequest,
|
flwr/server/app.py
CHANGED
|
@@ -301,6 +301,7 @@ def run_superlink() -> None:
|
|
|
301
301
|
fleet_server = _run_fleet_api_grpc_rere(
|
|
302
302
|
address=fleet_address,
|
|
303
303
|
state_factory=state_factory,
|
|
304
|
+
ffs_factory=ffs_factory,
|
|
304
305
|
certificates=certificates,
|
|
305
306
|
interceptors=interceptors,
|
|
306
307
|
)
|
|
@@ -487,6 +488,7 @@ def _try_obtain_certificates(
|
|
|
487
488
|
def _run_fleet_api_grpc_rere(
|
|
488
489
|
address: str,
|
|
489
490
|
state_factory: StateFactory,
|
|
491
|
+
ffs_factory: FfsFactory,
|
|
490
492
|
certificates: Optional[Tuple[bytes, bytes, bytes]],
|
|
491
493
|
interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
|
|
492
494
|
) -> grpc.Server:
|
|
@@ -494,6 +496,7 @@ def _run_fleet_api_grpc_rere(
|
|
|
494
496
|
# Create Fleet API gRPC server
|
|
495
497
|
fleet_servicer = FleetServicer(
|
|
496
498
|
state_factory=state_factory,
|
|
499
|
+
ffs_factory=ffs_factory,
|
|
497
500
|
)
|
|
498
501
|
fleet_add_servicer_to_server_fn = add_FleetServicer_to_server
|
|
499
502
|
fleet_grpc_server = generic_create_grpc_server(
|
flwr/server/run_serverapp.py
CHANGED
|
@@ -21,6 +21,8 @@ from logging import DEBUG, INFO, WARN
|
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
from typing import Optional
|
|
23
23
|
|
|
24
|
+
from flwr.cli.config_utils import get_fab_metadata
|
|
25
|
+
from flwr.cli.install import install_from_fab
|
|
24
26
|
from flwr.common import Context, EventType, RecordSet, event
|
|
25
27
|
from flwr.common.config import (
|
|
26
28
|
get_flwr_dir,
|
|
@@ -36,6 +38,7 @@ from flwr.proto.driver_pb2 import ( # pylint: disable=E0611
|
|
|
36
38
|
CreateRunRequest,
|
|
37
39
|
CreateRunResponse,
|
|
38
40
|
)
|
|
41
|
+
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
|
39
42
|
|
|
40
43
|
from .driver import Driver
|
|
41
44
|
from .driver.grpc_driver import GrpcDriver
|
|
@@ -87,7 +90,8 @@ def run(
|
|
|
87
90
|
log(DEBUG, "ServerApp finished running.")
|
|
88
91
|
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
# pylint: disable-next=too-many-branches,too-many-statements,too-many-locals
|
|
94
|
+
def run_server_app() -> None:
|
|
91
95
|
"""Run Flower server app."""
|
|
92
96
|
event(EventType.RUN_SERVER_APP_ENTER)
|
|
93
97
|
|
|
@@ -164,7 +168,19 @@ def run_server_app() -> None: # pylint: disable=too-many-branches
|
|
|
164
168
|
)
|
|
165
169
|
flwr_dir = get_flwr_dir(args.flwr_dir)
|
|
166
170
|
run_ = driver.run
|
|
167
|
-
|
|
171
|
+
if run_.fab_hash:
|
|
172
|
+
fab_req = GetFabRequest(hash_str=run_.fab_hash)
|
|
173
|
+
# pylint: disable-next=W0212
|
|
174
|
+
fab_res: GetFabResponse = driver._stub.GetFab(fab_req)
|
|
175
|
+
if fab_res.fab.hash_str != run_.fab_hash:
|
|
176
|
+
raise ValueError("FAB hashes don't match.")
|
|
177
|
+
|
|
178
|
+
install_from_fab(fab_res.fab.content, flwr_dir, True)
|
|
179
|
+
fab_id, fab_version = get_fab_metadata(fab_res.fab.content)
|
|
180
|
+
else:
|
|
181
|
+
fab_id, fab_version = run_.fab_id, run_.fab_version
|
|
182
|
+
|
|
183
|
+
app_path = str(get_project_dir(fab_id, fab_version, flwr_dir))
|
|
168
184
|
config = get_project_config(app_path)
|
|
169
185
|
else:
|
|
170
186
|
# User provided `app_dir`, but not `--run-id`
|
flwr/server/server.py
CHANGED
|
@@ -91,7 +91,7 @@ class Server:
|
|
|
91
91
|
# Initialize parameters
|
|
92
92
|
log(INFO, "[INIT]")
|
|
93
93
|
self.parameters = self._get_initial_parameters(server_round=0, timeout=timeout)
|
|
94
|
-
log(INFO, "
|
|
94
|
+
log(INFO, "Starting evaluation of initial global parameters")
|
|
95
95
|
res = self.strategy.evaluate(0, parameters=self.parameters)
|
|
96
96
|
if res is not None:
|
|
97
97
|
log(
|
|
@@ -102,6 +102,8 @@ class Server:
|
|
|
102
102
|
)
|
|
103
103
|
history.add_loss_centralized(server_round=0, loss=res[0])
|
|
104
104
|
history.add_metrics_centralized(server_round=0, metrics=res[1])
|
|
105
|
+
else:
|
|
106
|
+
log(INFO, "Evaluation returned no results (`None`)")
|
|
105
107
|
|
|
106
108
|
# Run federated learning for num_rounds
|
|
107
109
|
start_time = timeit.default_timer()
|