flwr-nightly 1.8.0.dev20240220__py3-none-any.whl → 1.8.0.dev20240222__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/client/app.py +1 -3
- flwr/common/__init__.py +2 -0
- flwr/common/constant.py +14 -0
- flwr/common/record/__init__.py +2 -0
- flwr/common/record/conversion_utils.py +40 -0
- flwr/common/record/parametersrecord.py +19 -2
- flwr/common/version.py +0 -1
- flwr/server/app.py +76 -44
- flwr/server/compat/app.py +1 -2
- flwr/server/server.py +45 -2
- flwr/server/strategy/dpfedavg_fixed.py +3 -3
- flwr/server/superlink/fleet/vce/__init__.py +21 -0
- flwr/server/superlink/fleet/vce/vce_api.py +71 -0
- flwr/simulation/app.py +3 -4
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/METADATA +5 -4
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/RECORD +19 -16
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/entry_points.txt +0 -0
flwr/client/app.py
CHANGED
@@ -506,9 +506,7 @@ def start_numpy_client(
|
|
506
506
|
)
|
507
507
|
|
508
508
|
|
509
|
-
def _init_connection(
|
510
|
-
transport: Optional[str], server_address: str
|
511
|
-
) -> Tuple[
|
509
|
+
def _init_connection(transport: Optional[str], server_address: str) -> Tuple[
|
512
510
|
Callable[
|
513
511
|
[str, bool, int, Union[bytes, str, None]],
|
514
512
|
ContextManager[
|
flwr/common/__init__.py
CHANGED
@@ -31,6 +31,7 @@ from .record import ConfigsRecord as ConfigsRecord
|
|
31
31
|
from .record import MetricsRecord as MetricsRecord
|
32
32
|
from .record import ParametersRecord as ParametersRecord
|
33
33
|
from .record import RecordSet as RecordSet
|
34
|
+
from .record import array_from_numpy as array_from_numpy
|
34
35
|
from .telemetry import EventType as EventType
|
35
36
|
from .telemetry import event as event
|
36
37
|
from .typing import ClientMessage as ClientMessage
|
@@ -58,6 +59,7 @@ from .typing import Status as Status
|
|
58
59
|
|
59
60
|
__all__ = [
|
60
61
|
"Array",
|
62
|
+
"array_from_numpy",
|
61
63
|
"bytes_to_ndarray",
|
62
64
|
"ClientMessage",
|
63
65
|
"Code",
|
flwr/common/constant.py
CHANGED
@@ -15,6 +15,8 @@
|
|
15
15
|
"""Flower constants."""
|
16
16
|
|
17
17
|
|
18
|
+
from __future__ import annotations
|
19
|
+
|
18
20
|
MISSING_EXTRA_REST = """
|
19
21
|
Extra dependencies required for using the REST-based Fleet API are missing.
|
20
22
|
|
@@ -26,13 +28,25 @@ To use the REST API, install `flwr` with the `rest` extra:
|
|
26
28
|
TRANSPORT_TYPE_GRPC_BIDI = "grpc-bidi"
|
27
29
|
TRANSPORT_TYPE_GRPC_RERE = "grpc-rere"
|
28
30
|
TRANSPORT_TYPE_REST = "rest"
|
31
|
+
TRANSPORT_TYPE_VCE = "vce"
|
29
32
|
TRANSPORT_TYPES = [
|
30
33
|
TRANSPORT_TYPE_GRPC_BIDI,
|
31
34
|
TRANSPORT_TYPE_GRPC_RERE,
|
32
35
|
TRANSPORT_TYPE_REST,
|
36
|
+
TRANSPORT_TYPE_VCE,
|
33
37
|
]
|
34
38
|
|
35
39
|
MESSAGE_TYPE_GET_PROPERTIES = "get_properties"
|
36
40
|
MESSAGE_TYPE_GET_PARAMETERS = "get_parameters"
|
37
41
|
MESSAGE_TYPE_FIT = "fit"
|
38
42
|
MESSAGE_TYPE_EVALUATE = "evaluate"
|
43
|
+
|
44
|
+
|
45
|
+
class SType:
|
46
|
+
"""Serialisation type."""
|
47
|
+
|
48
|
+
NUMPY = "numpy.ndarray"
|
49
|
+
|
50
|
+
def __new__(cls) -> SType:
|
51
|
+
"""Prevent instantiation."""
|
52
|
+
raise TypeError(f"{cls.__name__} cannot be instantiated.")
|
flwr/common/record/__init__.py
CHANGED
@@ -15,12 +15,14 @@
|
|
15
15
|
"""Record APIs."""
|
16
16
|
|
17
17
|
from .configsrecord import ConfigsRecord
|
18
|
+
from .conversion_utils import array_from_numpy
|
18
19
|
from .metricsrecord import MetricsRecord
|
19
20
|
from .parametersrecord import Array, ParametersRecord
|
20
21
|
from .recordset import RecordSet
|
21
22
|
|
22
23
|
__all__ = [
|
23
24
|
"Array",
|
25
|
+
"array_from_numpy",
|
24
26
|
"ConfigsRecord",
|
25
27
|
"MetricsRecord",
|
26
28
|
"ParametersRecord",
|
@@ -0,0 +1,40 @@
|
|
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
|
+
"""Conversion utility functions for Records."""
|
16
|
+
|
17
|
+
|
18
|
+
from io import BytesIO
|
19
|
+
|
20
|
+
import numpy as np
|
21
|
+
|
22
|
+
from ..constant import SType
|
23
|
+
from ..typing import NDArray
|
24
|
+
from .parametersrecord import Array
|
25
|
+
|
26
|
+
|
27
|
+
def array_from_numpy(ndarray: NDArray) -> Array:
|
28
|
+
"""Create Array from NumPy ndarray."""
|
29
|
+
buffer = BytesIO()
|
30
|
+
# WARNING: NEVER set allow_pickle to true.
|
31
|
+
# Reason: loading pickled data can execute arbitrary code
|
32
|
+
# Source: https://numpy.org/doc/stable/reference/generated/numpy.save.html
|
33
|
+
np.save(buffer, ndarray, allow_pickle=False)
|
34
|
+
data = buffer.getvalue()
|
35
|
+
return Array(
|
36
|
+
dtype=str(ndarray.dtype),
|
37
|
+
shape=list(ndarray.shape),
|
38
|
+
stype=SType.NUMPY,
|
39
|
+
data=data,
|
40
|
+
)
|
@@ -14,10 +14,14 @@
|
|
14
14
|
# ==============================================================================
|
15
15
|
"""ParametersRecord and Array."""
|
16
16
|
|
17
|
-
|
18
17
|
from dataclasses import dataclass
|
19
|
-
from
|
18
|
+
from io import BytesIO
|
19
|
+
from typing import List, Optional, OrderedDict, cast
|
20
|
+
|
21
|
+
import numpy as np
|
20
22
|
|
23
|
+
from ..constant import SType
|
24
|
+
from ..typing import NDArray
|
21
25
|
from .typeddict import TypedDict
|
22
26
|
|
23
27
|
|
@@ -51,6 +55,19 @@ class Array:
|
|
51
55
|
stype: str
|
52
56
|
data: bytes
|
53
57
|
|
58
|
+
def numpy(self) -> NDArray:
|
59
|
+
"""Return the array as a NumPy array."""
|
60
|
+
if self.stype != SType.NUMPY:
|
61
|
+
raise TypeError(
|
62
|
+
f"Unsupported serialization type for numpy conversion: '{self.stype}'"
|
63
|
+
)
|
64
|
+
bytes_io = BytesIO(self.data)
|
65
|
+
# WARNING: NEVER set allow_pickle to true.
|
66
|
+
# Reason: loading pickled data can execute arbitrary code
|
67
|
+
# Source: https://numpy.org/doc/stable/reference/generated/numpy.load.html
|
68
|
+
ndarray_deserialized = np.load(bytes_io, allow_pickle=False)
|
69
|
+
return cast(NDArray, ndarray_deserialized)
|
70
|
+
|
54
71
|
|
55
72
|
def _check_key(key: str) -> None:
|
56
73
|
"""Check if key is of expected type."""
|
flwr/common/version.py
CHANGED
flwr/server/app.py
CHANGED
@@ -34,6 +34,7 @@ from flwr.common.constant import (
|
|
34
34
|
MISSING_EXTRA_REST,
|
35
35
|
TRANSPORT_TYPE_GRPC_RERE,
|
36
36
|
TRANSPORT_TYPE_REST,
|
37
|
+
TRANSPORT_TYPE_VCE,
|
37
38
|
)
|
38
39
|
from flwr.common.logger import log
|
39
40
|
from flwr.proto.driver_pb2_grpc import ( # pylint: disable=E0611
|
@@ -43,17 +44,18 @@ from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
|
|
43
44
|
add_FleetServicer_to_server,
|
44
45
|
)
|
45
46
|
|
46
|
-
from .client_manager import ClientManager
|
47
|
+
from .client_manager import ClientManager
|
47
48
|
from .history import History
|
48
|
-
from .server import Server
|
49
|
+
from .server import Server, init_defaults, run_fl
|
49
50
|
from .server_config import ServerConfig
|
50
|
-
from .strategy import
|
51
|
+
from .strategy import Strategy
|
51
52
|
from .superlink.driver.driver_servicer import DriverServicer
|
52
53
|
from .superlink.fleet.grpc_bidi.grpc_server import (
|
53
54
|
generic_create_grpc_server,
|
54
55
|
start_grpc_server,
|
55
56
|
)
|
56
57
|
from .superlink.fleet.grpc_rere.fleet_servicer import FleetServicer
|
58
|
+
from .superlink.fleet.vce import start_vce
|
57
59
|
from .superlink.state import StateFactory
|
58
60
|
|
59
61
|
ADDRESS_DRIVER_API = "0.0.0.0:9091"
|
@@ -183,47 +185,6 @@ def start_server( # pylint: disable=too-many-arguments,too-many-locals
|
|
183
185
|
return hist
|
184
186
|
|
185
187
|
|
186
|
-
def init_defaults(
|
187
|
-
server: Optional[Server],
|
188
|
-
config: Optional[ServerConfig],
|
189
|
-
strategy: Optional[Strategy],
|
190
|
-
client_manager: Optional[ClientManager],
|
191
|
-
) -> Tuple[Server, ServerConfig]:
|
192
|
-
"""Create server instance if none was given."""
|
193
|
-
if server is None:
|
194
|
-
if client_manager is None:
|
195
|
-
client_manager = SimpleClientManager()
|
196
|
-
if strategy is None:
|
197
|
-
strategy = FedAvg()
|
198
|
-
server = Server(client_manager=client_manager, strategy=strategy)
|
199
|
-
elif strategy is not None:
|
200
|
-
log(WARN, "Both server and strategy were provided, ignoring strategy")
|
201
|
-
|
202
|
-
# Set default config values
|
203
|
-
if config is None:
|
204
|
-
config = ServerConfig()
|
205
|
-
|
206
|
-
return server, config
|
207
|
-
|
208
|
-
|
209
|
-
def run_fl(
|
210
|
-
server: Server,
|
211
|
-
config: ServerConfig,
|
212
|
-
) -> History:
|
213
|
-
"""Train a model on the given server and return the History object."""
|
214
|
-
hist = server.fit(num_rounds=config.num_rounds, timeout=config.round_timeout)
|
215
|
-
log(INFO, "app_fit: losses_distributed %s", str(hist.losses_distributed))
|
216
|
-
log(INFO, "app_fit: metrics_distributed_fit %s", str(hist.metrics_distributed_fit))
|
217
|
-
log(INFO, "app_fit: metrics_distributed %s", str(hist.metrics_distributed))
|
218
|
-
log(INFO, "app_fit: losses_centralized %s", str(hist.losses_centralized))
|
219
|
-
log(INFO, "app_fit: metrics_centralized %s", str(hist.metrics_centralized))
|
220
|
-
|
221
|
-
# Graceful shutdown
|
222
|
-
server.disconnect_all_clients(timeout=config.round_timeout)
|
223
|
-
|
224
|
-
return hist
|
225
|
-
|
226
|
-
|
227
188
|
def run_driver_api() -> None:
|
228
189
|
"""Run Flower server (Driver API)."""
|
229
190
|
log(INFO, "Starting Flower server (Driver API)")
|
@@ -401,6 +362,15 @@ def run_superlink() -> None:
|
|
401
362
|
certificates=certificates,
|
402
363
|
)
|
403
364
|
grpc_servers.append(fleet_server)
|
365
|
+
elif args.fleet_api_type == TRANSPORT_TYPE_VCE:
|
366
|
+
_run_fleet_api_vce(
|
367
|
+
num_supernodes=args.num_supernodes,
|
368
|
+
client_app_module_name=args.client_app,
|
369
|
+
backend_name=args.backend,
|
370
|
+
backend_config_json_stream=args.backend_config,
|
371
|
+
working_dir=args.dir,
|
372
|
+
state_factory=state_factory,
|
373
|
+
)
|
404
374
|
else:
|
405
375
|
raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
|
406
376
|
|
@@ -537,6 +507,27 @@ def _run_fleet_api_grpc_rere(
|
|
537
507
|
return fleet_grpc_server
|
538
508
|
|
539
509
|
|
510
|
+
# pylint: disable=too-many-arguments
|
511
|
+
def _run_fleet_api_vce(
|
512
|
+
num_supernodes: int,
|
513
|
+
client_app_module_name: str,
|
514
|
+
backend_name: str,
|
515
|
+
backend_config_json_stream: str,
|
516
|
+
working_dir: str,
|
517
|
+
state_factory: StateFactory,
|
518
|
+
) -> None:
|
519
|
+
log(INFO, "Flower VCE: Starting Fleet API (VirtualClientEngine)")
|
520
|
+
|
521
|
+
start_vce(
|
522
|
+
num_supernodes=num_supernodes,
|
523
|
+
client_app_module_name=client_app_module_name,
|
524
|
+
backend_name=backend_name,
|
525
|
+
backend_config_json_stream=backend_config_json_stream,
|
526
|
+
state_factory=state_factory,
|
527
|
+
working_dir=working_dir,
|
528
|
+
)
|
529
|
+
|
530
|
+
|
540
531
|
# pylint: disable=import-outside-toplevel,too-many-arguments
|
541
532
|
def _run_fleet_api_rest(
|
542
533
|
host: str,
|
@@ -714,6 +705,14 @@ def _add_args_fleet_api(parser: argparse.ArgumentParser) -> None:
|
|
714
705
|
help="Start a Fleet API server (REST, experimental)",
|
715
706
|
)
|
716
707
|
|
708
|
+
ex_group.add_argument(
|
709
|
+
"--vce",
|
710
|
+
action="store_const",
|
711
|
+
dest="fleet_api_type",
|
712
|
+
const=TRANSPORT_TYPE_VCE,
|
713
|
+
help="Start a Fleet API server (VirtualClientEngine)",
|
714
|
+
)
|
715
|
+
|
717
716
|
# Fleet API gRPC-rere options
|
718
717
|
grpc_rere_group = parser.add_argument_group(
|
719
718
|
"Fleet API (gRPC-rere) server options", ""
|
@@ -749,3 +748,36 @@ def _add_args_fleet_api(parser: argparse.ArgumentParser) -> None:
|
|
749
748
|
type=int,
|
750
749
|
default=1,
|
751
750
|
)
|
751
|
+
|
752
|
+
# Fleet API VCE options
|
753
|
+
vce_group = parser.add_argument_group("Fleet API (VCE) server options", "")
|
754
|
+
vce_group.add_argument(
|
755
|
+
"--client-app",
|
756
|
+
help="For example: `client:app` or `project.package.module:wrapper.app`.",
|
757
|
+
)
|
758
|
+
vce_group.add_argument(
|
759
|
+
"--num-supernodes",
|
760
|
+
type=int,
|
761
|
+
help="Number of simulated SuperNodes.",
|
762
|
+
)
|
763
|
+
vce_group.add_argument(
|
764
|
+
"--backend",
|
765
|
+
default="ray",
|
766
|
+
type=str,
|
767
|
+
help="Simulation backend that executes the ClientApp.",
|
768
|
+
)
|
769
|
+
vce_group.add_argument(
|
770
|
+
"--backend-config",
|
771
|
+
type=str,
|
772
|
+
default='{"client_resources": {"num_cpus":1, "num_gpus":0.0}, "tensorflow": 0}',
|
773
|
+
help='A JSON formatted stream, e.g \'{"<keyA>":<value>, "<keyB>":<value>}\' to '
|
774
|
+
"configure a backend. Values supported in <value> are those included by "
|
775
|
+
"`flwr.common.typing.ConfigsRecordValues`. ",
|
776
|
+
)
|
777
|
+
parser.add_argument(
|
778
|
+
"--dir",
|
779
|
+
default="",
|
780
|
+
help="Add specified directory to the PYTHONPATH and load"
|
781
|
+
"ClientApp from there."
|
782
|
+
" Default: current working directory.",
|
783
|
+
)
|
flwr/server/compat/app.py
CHANGED
@@ -26,10 +26,9 @@ from flwr.common import EventType, event
|
|
26
26
|
from flwr.common.address import parse_address
|
27
27
|
from flwr.common.logger import log, warn_deprecated_feature
|
28
28
|
from flwr.proto import driver_pb2 # pylint: disable=E0611
|
29
|
-
from flwr.server.app import init_defaults, run_fl
|
30
29
|
from flwr.server.client_manager import ClientManager
|
31
30
|
from flwr.server.history import History
|
32
|
-
from flwr.server.server import Server
|
31
|
+
from flwr.server.server import Server, init_defaults, run_fl
|
33
32
|
from flwr.server.server_config import ServerConfig
|
34
33
|
from flwr.server.strategy import Strategy
|
35
34
|
|
flwr/server/server.py
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
import concurrent.futures
|
19
19
|
import timeit
|
20
|
-
from logging import DEBUG, INFO
|
20
|
+
from logging import DEBUG, INFO, WARN
|
21
21
|
from typing import Dict, List, Optional, Tuple, Union
|
22
22
|
|
23
23
|
from flwr.common import (
|
@@ -33,11 +33,13 @@ from flwr.common import (
|
|
33
33
|
)
|
34
34
|
from flwr.common.logger import log
|
35
35
|
from flwr.common.typing import GetParametersIns
|
36
|
-
from flwr.server.client_manager import ClientManager
|
36
|
+
from flwr.server.client_manager import ClientManager, SimpleClientManager
|
37
37
|
from flwr.server.client_proxy import ClientProxy
|
38
38
|
from flwr.server.history import History
|
39
39
|
from flwr.server.strategy import FedAvg, Strategy
|
40
40
|
|
41
|
+
from .server_config import ServerConfig
|
42
|
+
|
41
43
|
FitResultsAndFailures = Tuple[
|
42
44
|
List[Tuple[ClientProxy, FitRes]],
|
43
45
|
List[Union[Tuple[ClientProxy, FitRes], BaseException]],
|
@@ -441,3 +443,44 @@ def _handle_finished_future_after_evaluate(
|
|
441
443
|
|
442
444
|
# Not successful, client returned a result where the status code is not OK
|
443
445
|
failures.append(result)
|
446
|
+
|
447
|
+
|
448
|
+
def init_defaults(
|
449
|
+
server: Optional[Server],
|
450
|
+
config: Optional[ServerConfig],
|
451
|
+
strategy: Optional[Strategy],
|
452
|
+
client_manager: Optional[ClientManager],
|
453
|
+
) -> Tuple[Server, ServerConfig]:
|
454
|
+
"""Create server instance if none was given."""
|
455
|
+
if server is None:
|
456
|
+
if client_manager is None:
|
457
|
+
client_manager = SimpleClientManager()
|
458
|
+
if strategy is None:
|
459
|
+
strategy = FedAvg()
|
460
|
+
server = Server(client_manager=client_manager, strategy=strategy)
|
461
|
+
elif strategy is not None:
|
462
|
+
log(WARN, "Both server and strategy were provided, ignoring strategy")
|
463
|
+
|
464
|
+
# Set default config values
|
465
|
+
if config is None:
|
466
|
+
config = ServerConfig()
|
467
|
+
|
468
|
+
return server, config
|
469
|
+
|
470
|
+
|
471
|
+
def run_fl(
|
472
|
+
server: Server,
|
473
|
+
config: ServerConfig,
|
474
|
+
) -> History:
|
475
|
+
"""Train a model on the given server and return the History object."""
|
476
|
+
hist = server.fit(num_rounds=config.num_rounds, timeout=config.round_timeout)
|
477
|
+
log(INFO, "app_fit: losses_distributed %s", str(hist.losses_distributed))
|
478
|
+
log(INFO, "app_fit: metrics_distributed_fit %s", str(hist.metrics_distributed_fit))
|
479
|
+
log(INFO, "app_fit: metrics_distributed %s", str(hist.metrics_distributed))
|
480
|
+
log(INFO, "app_fit: losses_centralized %s", str(hist.losses_centralized))
|
481
|
+
log(INFO, "app_fit: metrics_centralized %s", str(hist.metrics_centralized))
|
482
|
+
|
483
|
+
# Graceful shutdown
|
484
|
+
server.disconnect_all_clients(timeout=config.round_timeout)
|
485
|
+
|
486
|
+
return hist
|
@@ -104,9 +104,9 @@ class DPFedAvgFixed(Strategy):
|
|
104
104
|
"""
|
105
105
|
additional_config = {"dpfedavg_clip_norm": self.clip_norm}
|
106
106
|
if not self.server_side_noising:
|
107
|
-
additional_config[
|
108
|
-
|
109
|
-
|
107
|
+
additional_config["dpfedavg_noise_stddev"] = (
|
108
|
+
self._calc_client_noise_stddev()
|
109
|
+
)
|
110
110
|
|
111
111
|
client_instructions = self.strategy.configure_fit(
|
112
112
|
server_round, parameters, client_manager
|
@@ -0,0 +1,21 @@
|
|
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
|
+
"""Fleet VirtualClientEngine side."""
|
16
|
+
|
17
|
+
from .vce_api import start_vce
|
18
|
+
|
19
|
+
__all__ = [
|
20
|
+
"start_vce",
|
21
|
+
]
|
@@ -0,0 +1,71 @@
|
|
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
|
+
"""Fleet VirtualClientEngine API."""
|
16
|
+
|
17
|
+
import json
|
18
|
+
from logging import INFO
|
19
|
+
from typing import Dict
|
20
|
+
|
21
|
+
from flwr.client.clientapp import ClientApp, load_client_app
|
22
|
+
from flwr.client.node_state import NodeState
|
23
|
+
from flwr.common.logger import log
|
24
|
+
from flwr.server.superlink.state import StateFactory
|
25
|
+
|
26
|
+
NodeToPartitionMapping = Dict[int, int]
|
27
|
+
|
28
|
+
|
29
|
+
def _register_nodes(
|
30
|
+
num_nodes: int, state_factory: StateFactory
|
31
|
+
) -> NodeToPartitionMapping:
|
32
|
+
"""Register nodes with the StateFactory and create node-id:partition-id mapping."""
|
33
|
+
nodes_mapping: NodeToPartitionMapping = {}
|
34
|
+
state = state_factory.state()
|
35
|
+
for i in range(num_nodes):
|
36
|
+
node_id = state.create_node()
|
37
|
+
nodes_mapping[node_id] = i
|
38
|
+
log(INFO, "Registered %i nodes", len(nodes_mapping))
|
39
|
+
return nodes_mapping
|
40
|
+
|
41
|
+
|
42
|
+
# pylint: disable=too-many-arguments,unused-argument
|
43
|
+
def start_vce(
|
44
|
+
num_supernodes: int,
|
45
|
+
client_app_module_name: str,
|
46
|
+
backend_name: str,
|
47
|
+
backend_config_json_stream: str,
|
48
|
+
state_factory: StateFactory,
|
49
|
+
working_dir: str,
|
50
|
+
) -> None:
|
51
|
+
"""Start Fleet API with the VirtualClientEngine (VCE)."""
|
52
|
+
# Register SuperNodes
|
53
|
+
nodes_mapping = _register_nodes(
|
54
|
+
num_nodes=num_supernodes, state_factory=state_factory
|
55
|
+
)
|
56
|
+
|
57
|
+
# Construct mapping of NodeStates
|
58
|
+
node_states: Dict[int, NodeState] = {}
|
59
|
+
for node_id in nodes_mapping:
|
60
|
+
node_states[node_id] = NodeState()
|
61
|
+
|
62
|
+
# Load backend config
|
63
|
+
_ = json.loads(backend_config_json_stream)
|
64
|
+
|
65
|
+
log(INFO, "client_app_str = %s", client_app_module_name)
|
66
|
+
|
67
|
+
def _load() -> ClientApp:
|
68
|
+
app: ClientApp = load_client_app(client_app_module_name)
|
69
|
+
return app
|
70
|
+
|
71
|
+
# start backend
|
flwr/simulation/app.py
CHANGED
@@ -28,10 +28,9 @@ from ray.util.scheduling_strategies import NodeAffinitySchedulingStrategy
|
|
28
28
|
from flwr.client import ClientFn
|
29
29
|
from flwr.common import EventType, event
|
30
30
|
from flwr.common.logger import log
|
31
|
-
from flwr.server import Server
|
32
|
-
from flwr.server.app import init_defaults, run_fl
|
33
31
|
from flwr.server.client_manager import ClientManager
|
34
32
|
from flwr.server.history import History
|
33
|
+
from flwr.server.server import Server, init_defaults, run_fl
|
35
34
|
from flwr.server.server_config import ServerConfig
|
36
35
|
from flwr.server.strategy import Strategy
|
37
36
|
from flwr.simulation.ray_transport.ray_actor import (
|
@@ -220,7 +219,7 @@ def start_simulation(
|
|
220
219
|
log(
|
221
220
|
INFO,
|
222
221
|
"Optimize your simulation with Flower VCE: "
|
223
|
-
"https://flower.
|
222
|
+
"https://flower.ai/docs/framework/how-to-run-simulations.html",
|
224
223
|
)
|
225
224
|
|
226
225
|
# Log the resources that a single client will be able to use
|
@@ -338,7 +337,7 @@ def start_simulation(
|
|
338
337
|
"disconnected. The head node might still be alive but cannot accommodate "
|
339
338
|
"any actor with resources: %s."
|
340
339
|
"\nTake a look at the Flower simulation examples for guidance "
|
341
|
-
"<https://flower.
|
340
|
+
"<https://flower.ai/docs/framework/how-to-run-simulations.html>.",
|
342
341
|
client_resources,
|
343
342
|
client_resources,
|
344
343
|
)
|
{flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/METADATA
RENAMED
@@ -1,12 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.8.0.
|
3
|
+
Version: 1.8.0.dev20240222
|
4
4
|
Summary: Flower: A Friendly Federated Learning Framework
|
5
|
-
Home-page: https://flower.
|
5
|
+
Home-page: https://flower.ai
|
6
6
|
License: Apache-2.0
|
7
7
|
Keywords: flower,fl,federated learning,federated analytics,federated evaluation,machine learning
|
8
8
|
Author: The Flower Authors
|
9
|
-
Author-email: hello@flower.
|
9
|
+
Author-email: hello@flower.ai
|
10
10
|
Requires-Python: >=3.8,<4.0
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
12
12
|
Classifier: Intended Audience :: Developers
|
@@ -44,7 +44,7 @@ Requires-Dist: requests (>=2.31.0,<3.0.0) ; extra == "rest"
|
|
44
44
|
Requires-Dist: starlette (>=0.31.0,<0.32.0) ; extra == "rest"
|
45
45
|
Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
|
46
46
|
Requires-Dist: uvicorn[standard] (>=0.23.0,<0.24.0) ; extra == "rest"
|
47
|
-
Project-URL: Documentation, https://flower.
|
47
|
+
Project-URL: Documentation, https://flower.ai
|
48
48
|
Project-URL: Repository, https://github.com/adap/flower
|
49
49
|
Description-Content-Type: text/markdown
|
50
50
|
|
@@ -199,6 +199,7 @@ Other [examples](https://github.com/adap/flower/tree/main/examples):
|
|
199
199
|
- Single-Machine Simulation of Federated Learning Systems ([PyTorch](https://github.com/adap/flower/tree/main/examples/simulation-pytorch)) ([Tensorflow](https://github.com/adap/flower/tree/main/examples/simulation-tensorflow))
|
200
200
|
- [Comprehensive Flower+XGBoost](https://github.com/adap/flower/tree/main/examples/xgboost-comprehensive)
|
201
201
|
- [Flower through Docker Compose and with Grafana dashboard](https://github.com/adap/flower/tree/main/examples/flower-via-docker-compose)
|
202
|
+
- [Flower with KaplanMeierFitter from the lifelines library](https://github.com/adap/flower/tree/main/examples/federated-kaplna-meier-fitter)
|
202
203
|
|
203
204
|
## Community
|
204
205
|
|
{flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/RECORD
RENAMED
@@ -18,7 +18,7 @@ flwr/cli/new/templates/app/requirements.pytorch.txt.tpl,sha256=9Z70jsiCPdsbuorhi
|
|
18
18
|
flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl,sha256=WTbIgK5G_iG0W-xtvQLCZMxL_Og26rFXial2TkYH5dw,211
|
19
19
|
flwr/cli/utils.py,sha256=9cCEIt8QmJXz85JNmPk1IHPd7p8E3KDn6h5CfF0nDL4,1926
|
20
20
|
flwr/client/__init__.py,sha256=ypNGqhSDxEPX_6XwcQyVseBf8oS_Zvs_JkrLbVIeKxw,1186
|
21
|
-
flwr/client/app.py,sha256=
|
21
|
+
flwr/client/app.py,sha256=BxQGhdhFiw9JrKu-TcgmyshN02K9QpgWPxFVEcQ4Mb8,19983
|
22
22
|
flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
|
23
23
|
flwr/client/clientapp.py,sha256=jrDgJBswP2hD1YdGgQoI3GU_NkliYWVU8glBJLOVzQY,4205
|
24
24
|
flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
|
@@ -39,9 +39,9 @@ flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,1
|
|
39
39
|
flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
|
40
40
|
flwr/client/rest_client/connection.py,sha256=363X5FZPg2H8f9dZk_IhUrVK-DGiDHfIodJNat-W8XE,11903
|
41
41
|
flwr/client/typing.py,sha256=c9EvjlEjasxn1Wqx6bGl6Xg6vM1gMFfmXht-E2i5J-k,1006
|
42
|
-
flwr/common/__init__.py,sha256=
|
42
|
+
flwr/common/__init__.py,sha256=Pm7NIXPNUxQGe8mMv9L7xFi5VohYnJEpgR-RP7gMDQQ,3466
|
43
43
|
flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
|
44
|
-
flwr/common/constant.py,sha256
|
44
|
+
flwr/common/constant.py,sha256=jVUVKXo1cFb2HpRYqV70WKMG4RqCVrq7H6KC7zXs23Y,1572
|
45
45
|
flwr/common/context.py,sha256=ounF-mWPPtXGwtae3sg5EhF58ScviOa3MVqxRpGVu-8,1313
|
46
46
|
flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
|
47
47
|
flwr/common/differential_privacy.py,sha256=pVSKRhciVNtdBlhoz1H0--8N5PMLjdO_bA1PLGq4WZ8,2969
|
@@ -51,10 +51,11 @@ flwr/common/grpc.py,sha256=qVLB0d6bCuaBRW5YB0vEZXsR7Bo3R2lh4ONiCocqwRI,2270
|
|
51
51
|
flwr/common/logger.py,sha256=qX_gqEyrmGOH0x_r8uQ1Vskz4fGvEij9asdo4DUOPY8,4135
|
52
52
|
flwr/common/message.py,sha256=tXkOAIYnPN5cW2OVggVTAUSaBxEDqoFGpST7uPpIAho,6198
|
53
53
|
flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
|
54
|
-
flwr/common/record/__init__.py,sha256=
|
54
|
+
flwr/common/record/__init__.py,sha256=33OaDW2bvaW952DFHH1amHclv4AuDZu385jXjHhXoog,1054
|
55
55
|
flwr/common/record/configsrecord.py,sha256=qL-hQ6ZFOOWJYCUHeFiao2vcO5rnk585Ns5Yxfb1sp4,3378
|
56
|
+
flwr/common/record/conversion_utils.py,sha256=n3I3SI2P6hUjyxbWNc0QAch-SEhfMK6Hm-UUaplAlUc,1393
|
56
57
|
flwr/common/record/metricsrecord.py,sha256=iEihUVNqwyZtbim7FfqJ7ksEbPef6cqTZ1w4-_C5afw,3360
|
57
|
-
flwr/common/record/parametersrecord.py,sha256=
|
58
|
+
flwr/common/record/parametersrecord.py,sha256=ii8Ol5W3dk_5YUWSFbbLC5KWep2eqlSWfCsgSM4Pedg,4331
|
58
59
|
flwr/common/record/recordset.py,sha256=OeRcBMGqx9vutWRz1xkujBPHlVpU58R1EcFRHEQrePo,2351
|
59
60
|
flwr/common/record/typeddict.py,sha256=2NW8JF27p1uNWaqDbJ7bMkItA5x4ygYT8aHrf8NaqnE,3879
|
60
61
|
flwr/common/recordset_compat.py,sha256=MXabdTaE9ItZ05oid6YLWtdbvgGHbTXsnMoTSJYYpcY,13842
|
@@ -70,7 +71,7 @@ flwr/common/secure_aggregation/secaggplus_utils.py,sha256=PleDyDu7jHNAfbRoEaoQiO
|
|
70
71
|
flwr/common/serde.py,sha256=0N2SG6T71rVIGPiUIBIZfyWyNGQTp-MjDpy9sDUvrXc,20622
|
71
72
|
flwr/common/telemetry.py,sha256=JkFB6WBOskqAJfzSM-l6tQfRiSi2oiysClfg0-5T7NY,7782
|
72
73
|
flwr/common/typing.py,sha256=3Wu6Ol1Ja6Gb0WdlcXVEn1EHYJbc4oRRJA81vEegxBo,4382
|
73
|
-
flwr/common/version.py,sha256=
|
74
|
+
flwr/common/version.py,sha256=_RDSMGZPEuGKYViZuXPotDtXMvh4iyDH9XOCO4qtPO8,666
|
74
75
|
flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
|
75
76
|
flwr/proto/driver_pb2.py,sha256=JHIdjNPTgp6YHD-_lz5ZZFB0VIOR3_GmcaOTN4jndc4,3115
|
76
77
|
flwr/proto/driver_pb2.pyi,sha256=xwl2AqIWn0SwAlg-x5RUQeqr6DC48eywnqmD7gbaaFs,4670
|
@@ -98,11 +99,11 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
98
99
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
99
100
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
100
101
|
flwr/server/__init__.py,sha256=_Wv3UkZzSXzbKWXyl2yY8tU6oqf_XqIruggpHjnmikE,1662
|
101
|
-
flwr/server/app.py,sha256=
|
102
|
+
flwr/server/app.py,sha256=0-p--_yF8rHTPxjrwZnBkEZgEeyAAqJ-i18vdCHTq5g,26328
|
102
103
|
flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
|
103
104
|
flwr/server/client_proxy.py,sha256=8ScGDvP3jHbl8DV3hyFID5N5VEVlXn8ZTQXtkdOfssI,2234
|
104
105
|
flwr/server/compat/__init__.py,sha256=KNvRFANbIc8LFRKHsBVfxcbOSekEImWgNq_gapCkbic,812
|
105
|
-
flwr/server/compat/app.py,sha256=
|
106
|
+
flwr/server/compat/app.py,sha256=lfPCbY4WVMvKdscBxraikBn97SEuSwR_dUDoTHTMe7w,7859
|
106
107
|
flwr/server/compat/driver_client_proxy.py,sha256=S3lI6Wi7fpkbg6GEi6TSzB6RHsrIdg1w44sLe-94UUk,6148
|
107
108
|
flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
|
108
109
|
flwr/server/driver/__init__.py,sha256=yYyVX1FcDiDFM6rw0-DSZpuRy0EoWRfG9puwlQUswFA,820
|
@@ -110,7 +111,7 @@ flwr/server/driver/driver.py,sha256=DApIRFeEzReZpdAsbFQ68haSUONMstblyOt6qcseMNc,
|
|
110
111
|
flwr/server/driver/grpc_driver.py,sha256=Ekcxx4Miqume02fiBjBRJhItaOFNpn120WT3uRIPWIc,4585
|
111
112
|
flwr/server/history.py,sha256=W7PHCFX7dLXrdnaVfl5V4tuzmtxh6zArkWYxVXvTZ1c,4904
|
112
113
|
flwr/server/run_serverapp.py,sha256=tYNbz11xMvkbcMty6u5nkLHmZhwIjSO78CfW-Aas5R8,4488
|
113
|
-
flwr/server/server.py,sha256=
|
114
|
+
flwr/server/server.py,sha256=kUIqgLIXnWcSrhEhXXkaZPRooYhTGGX-RDCYzG9J76g,17495
|
114
115
|
flwr/server/server_app.py,sha256=avNQ7AMMKsn09ly81C3UBgOfHhM_R29l4MrzlalGoj8,5892
|
115
116
|
flwr/server/server_config.py,sha256=yOHpkdyuhOm--Gy_4Vofvu6jCDxhyECEDpIy02beuCg,1018
|
116
117
|
flwr/server/strategy/__init__.py,sha256=qYOURtpO5DtaDXzvo0wd1lv0cP3wGQxbhqn3s5i8--k,2220
|
@@ -118,7 +119,7 @@ flwr/server/strategy/aggregate.py,sha256=QyRIJtI5gnuY1NbgrcrOvkHxGIxBvApq7d9Y4xl
|
|
118
119
|
flwr/server/strategy/bulyan.py,sha256=8GsSVJzRSoSWE2zQUKqC3Z795grdN9xpmc3MSGGXnzM,6532
|
119
120
|
flwr/server/strategy/dp_fixed_clipping.py,sha256=185P6F17uOtRByDaQSCpKj6jCGgP4271xhoMLUl2oR8,6671
|
120
121
|
flwr/server/strategy/dpfedavg_adaptive.py,sha256=hLJkPQJl1bHjwrBNg3PSRFKf3no0hg5EHiFaWhHlWqw,4877
|
121
|
-
flwr/server/strategy/dpfedavg_fixed.py,sha256=
|
122
|
+
flwr/server/strategy/dpfedavg_fixed.py,sha256=G0yYxrPoM-MHQ889DYN3OeNiEeU0yQrjgAzcq0G653w,7219
|
122
123
|
flwr/server/strategy/fault_tolerant_fedavg.py,sha256=veGcehB6rXT_MihNDrD1v5JY-TxJi7fybdDl-OZooDQ,5900
|
123
124
|
flwr/server/strategy/fedadagrad.py,sha256=9yoVdZOFTjQ7DpaVrYLH9ca88WgJVWepld6UXybGQMY,6505
|
124
125
|
flwr/server/strategy/fedadam.py,sha256=Zvqo6oChwB2aDGHeLXHNE74nHGwkFAWODLZ8f6Dtq1g,6763
|
@@ -151,6 +152,8 @@ flwr/server/superlink/fleet/message_handler/__init__.py,sha256=hEY0l61ojH8Iz30_K
|
|
151
152
|
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=iUBTJ2fzVL0XB8vfaEzCNJ6okeuOWrK6LJe__ElP9x8,2833
|
152
153
|
flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=VKDvDq5H8koOUztpmQacVzGJXPLEEkL1Vmolxt3mvnY,735
|
153
154
|
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=7JCs7NW4Qq8W5QhXxqsQNFiCLlRY-b_iD420vH1Mu-U,5906
|
155
|
+
flwr/server/superlink/fleet/vce/__init__.py,sha256=bogHbcWSXkD7wZkqUXiLRKQTJUs7jtr5uwaGlmoA-Yc,785
|
156
|
+
flwr/server/superlink/fleet/vce/vce_api.py,sha256=okSRawmpRD5FntvBYFgAv55dQtXJbZLzHr2XxEqGArM,2351
|
154
157
|
flwr/server/superlink/state/__init__.py,sha256=ij-7Ms-hyordQdRmGQxY1-nVa4OhixJ0jr7_YDkys0s,1003
|
155
158
|
flwr/server/superlink/state/in_memory_state.py,sha256=viGGGyg7LwwxKfCnndV6Tp9poVeZTrbN9z0tt-4qrqI,7903
|
156
159
|
flwr/server/superlink/state/sqlite_state.py,sha256=Adc2g1DecAN9Cl9F8lekuTb885mIHiOi6sQv4nxbmSc,21203
|
@@ -161,13 +164,13 @@ flwr/server/utils/__init__.py,sha256=RQVbo-bcsVtp_lJBf7dL5w01FbLrr7v3YedeGp5_YMs
|
|
161
164
|
flwr/server/utils/tensorboard.py,sha256=k0G6bqsLx7wfYbH2KtXsDYcOCfyIeE12-hefXA7lZdg,5485
|
162
165
|
flwr/server/utils/validator.py,sha256=IJN2475yyD_i_9kg_SJ_JodIuZh58ufpWGUDQRAqu2s,4740
|
163
166
|
flwr/simulation/__init__.py,sha256=E2eD5FlTmZZ80u21FmWCkacrM7O4mrEHD8iXqeCaBUQ,1278
|
164
|
-
flwr/simulation/app.py,sha256=
|
167
|
+
flwr/simulation/app.py,sha256=WqJxdXTEuehwMW605p5NMmvBbKYx5tuqnV3Mp7jSWXM,13904
|
165
168
|
flwr/simulation/ray_transport/__init__.py,sha256=FsaAnzC4cw4DqoouBCix6496k29jACkfeIam55BvW9g,734
|
166
169
|
flwr/simulation/ray_transport/ray_actor.py,sha256=mg-vsqt8w6ZtGLkHlz6SnZtZz5HcQY0gYiKSlzYT27A,16591
|
167
170
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=8I1g8nK0Vz7ygpOSzGZ1oXTfwi1vez8fxYWCdSXBrXE,6290
|
168
171
|
flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
|
169
|
-
flwr_nightly-1.8.0.
|
170
|
-
flwr_nightly-1.8.0.
|
171
|
-
flwr_nightly-1.8.0.
|
172
|
-
flwr_nightly-1.8.0.
|
173
|
-
flwr_nightly-1.8.0.
|
172
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
173
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/METADATA,sha256=GyA7xLs4BePK4FYLf19kYaR5vZT9SmSl3Qj5sZ6eosc,15040
|
174
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
175
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/entry_points.txt,sha256=S1zLNFLrz0uPWs4Zrgo2EPY0iQiIcCJHrIAlnQkkOBI,262
|
176
|
+
flwr_nightly-1.8.0.dev20240222.dist-info/RECORD,,
|
{flwr_nightly-1.8.0.dev20240220.dist-info → flwr_nightly-1.8.0.dev20240222.dist-info}/LICENSE
RENAMED
File without changes
|
File without changes
|
File without changes
|