flwr-nightly 1.9.0.dev20240418__py3-none-any.whl → 1.9.0.dev20240419__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 +33 -0
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +56 -0
- flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +18 -0
- flwr/client/__init__.py +2 -0
- flwr/client/app.py +7 -53
- flwr/client/grpc_client/connection.py +2 -1
- flwr/client/grpc_rere_client/connection.py +16 -2
- flwr/client/rest_client/connection.py +87 -168
- flwr/client/supernode/__init__.py +22 -0
- flwr/client/supernode/app.py +107 -0
- flwr/common/telemetry.py +4 -0
- flwr/server/app.py +5 -5
- flwr/server/superlink/fleet/message_handler/message_handler.py +4 -1
- {flwr_nightly-1.9.0.dev20240418.dist-info → flwr_nightly-1.9.0.dev20240419.dist-info}/METADATA +1 -1
- {flwr_nightly-1.9.0.dev20240418.dist-info → flwr_nightly-1.9.0.dev20240419.dist-info}/RECORD +18 -16
- {flwr_nightly-1.9.0.dev20240418.dist-info → flwr_nightly-1.9.0.dev20240419.dist-info}/entry_points.txt +1 -0
- {flwr_nightly-1.9.0.dev20240418.dist-info → flwr_nightly-1.9.0.dev20240419.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.9.0.dev20240418.dist-info → flwr_nightly-1.9.0.dev20240419.dist-info}/WHEEL +0 -0
flwr/cli/config_utils.py
CHANGED
|
@@ -15,13 +15,46 @@
|
|
|
15
15
|
"""Utility to validate the `pyproject.toml` file."""
|
|
16
16
|
|
|
17
17
|
import os
|
|
18
|
+
from pathlib import Path
|
|
18
19
|
from typing import Any, Dict, List, Optional, Tuple
|
|
19
20
|
|
|
20
21
|
import tomli
|
|
22
|
+
import typer
|
|
21
23
|
|
|
22
24
|
from flwr.common import object_ref
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
def validate_project_dir(project_dir: Path) -> Optional[Dict[str, Any]]:
|
|
28
|
+
"""Check if a Flower App directory is valid."""
|
|
29
|
+
config = load(str(project_dir / "pyproject.toml"))
|
|
30
|
+
if config is None:
|
|
31
|
+
typer.secho(
|
|
32
|
+
"❌ Project configuration could not be loaded. "
|
|
33
|
+
"`pyproject.toml` does not exist.",
|
|
34
|
+
fg=typer.colors.RED,
|
|
35
|
+
bold=True,
|
|
36
|
+
)
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
if not validate(config):
|
|
40
|
+
typer.secho(
|
|
41
|
+
"❌ Project configuration is invalid.",
|
|
42
|
+
fg=typer.colors.RED,
|
|
43
|
+
bold=True,
|
|
44
|
+
)
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
if "publisher" not in config["flower"]:
|
|
48
|
+
typer.secho(
|
|
49
|
+
"❌ Project configuration is missing required `publisher` field.",
|
|
50
|
+
fg=typer.colors.RED,
|
|
51
|
+
bold=True,
|
|
52
|
+
)
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
return config
|
|
56
|
+
|
|
57
|
+
|
|
25
58
|
def load_and_validate_with_defaults(
|
|
26
59
|
path: Optional[str] = None,
|
|
27
60
|
) -> Tuple[Optional[Dict[str, Any]], List[str], List[str]]:
|
|
@@ -1 +1,57 @@
|
|
|
1
1
|
"""$project_name: A Flower / TensorFlow app."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import tensorflow as tf
|
|
6
|
+
from flwr.client import ClientApp, NumPyClient
|
|
7
|
+
from flwr_datasets import FederatedDataset
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
|
|
11
|
+
|
|
12
|
+
# Define Flower client
|
|
13
|
+
class FlowerClient(NumPyClient):
|
|
14
|
+
def __init__(self, model, train_data, test_data):
|
|
15
|
+
self.model = model
|
|
16
|
+
self.x_train, self.y_train = train_data
|
|
17
|
+
self.x_test, self.y_test = test_data
|
|
18
|
+
|
|
19
|
+
def get_parameters(self, config):
|
|
20
|
+
return self.model.get_weights()
|
|
21
|
+
|
|
22
|
+
def fit(self, parameters, config):
|
|
23
|
+
self.model.set_weights(parameters)
|
|
24
|
+
self.model.fit(self.x_train, self.y_train, epochs=1, batch_size=32, verbose=0)
|
|
25
|
+
return self.model.get_weights(), len(self.x_train), {}
|
|
26
|
+
|
|
27
|
+
def evaluate(self, parameters, config):
|
|
28
|
+
self.model.set_weights(parameters)
|
|
29
|
+
loss, accuracy = self.model.evaluate(self.x_test, self.y_test, verbose=0)
|
|
30
|
+
return loss, len(self.x_test), {"accuracy": accuracy}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
fds = FederatedDataset(dataset="cifar10", partitioners={"train": 2})
|
|
34
|
+
|
|
35
|
+
def client_fn(cid: str):
|
|
36
|
+
"""Create and return an instance of Flower `Client`."""
|
|
37
|
+
|
|
38
|
+
# Load model and data (MobileNetV2, CIFAR-10)
|
|
39
|
+
model = tf.keras.applications.MobileNetV2((32, 32, 3), classes=10, weights=None)
|
|
40
|
+
model.compile("adam", "sparse_categorical_crossentropy", metrics=["accuracy"])
|
|
41
|
+
|
|
42
|
+
# Download and partition dataset
|
|
43
|
+
partition = fds.load_partition(int(cid), "train")
|
|
44
|
+
partition.set_format("numpy")
|
|
45
|
+
|
|
46
|
+
# Divide data on each node: 80% train, 20% test
|
|
47
|
+
partition = partition.train_test_split(test_size=0.2, seed=42)
|
|
48
|
+
train_data = partition["train"]["img"] / 255.0, partition["train"]["label"]
|
|
49
|
+
test_data = partition["test"]["img"] / 255.0, partition["test"]["label"]
|
|
50
|
+
|
|
51
|
+
return FlowerClient(model, train_data, test_data).to_client()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Flower ClientApp
|
|
55
|
+
app = ClientApp(
|
|
56
|
+
client_fn=client_fn,
|
|
57
|
+
)
|
|
@@ -1 +1,19 @@
|
|
|
1
1
|
"""$project_name: A Flower / TensorFlow app."""
|
|
2
|
+
|
|
3
|
+
from flwr.server import ServerApp, ServerConfig
|
|
4
|
+
from flwr.server.strategy import FedAvg
|
|
5
|
+
|
|
6
|
+
# Define config
|
|
7
|
+
config = ServerConfig(num_rounds=3)
|
|
8
|
+
|
|
9
|
+
strategy = FedAvg(
|
|
10
|
+
fraction_fit=1.0,
|
|
11
|
+
fraction_evaluate=1.0,
|
|
12
|
+
min_available_clients=2,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
# Flower ServerApp
|
|
16
|
+
app = ServerApp(
|
|
17
|
+
config=config,
|
|
18
|
+
strategy=strategy,
|
|
19
|
+
)
|
flwr/client/__init__.py
CHANGED
|
@@ -21,6 +21,7 @@ from .app import start_numpy_client as start_numpy_client
|
|
|
21
21
|
from .client import Client as Client
|
|
22
22
|
from .client_app import ClientApp as ClientApp
|
|
23
23
|
from .numpy_client import NumPyClient as NumPyClient
|
|
24
|
+
from .supernode import run_supernode as run_supernode
|
|
24
25
|
from .typing import ClientFn as ClientFn
|
|
25
26
|
|
|
26
27
|
__all__ = [
|
|
@@ -29,6 +30,7 @@ __all__ = [
|
|
|
29
30
|
"ClientFn",
|
|
30
31
|
"NumPyClient",
|
|
31
32
|
"run_client_app",
|
|
33
|
+
"run_supernode",
|
|
32
34
|
"start_client",
|
|
33
35
|
"start_numpy_client",
|
|
34
36
|
]
|
flwr/client/app.py
CHANGED
|
@@ -47,14 +47,15 @@ from .grpc_rere_client.connection import grpc_request_response
|
|
|
47
47
|
from .message_handler.message_handler import handle_control_message
|
|
48
48
|
from .node_state import NodeState
|
|
49
49
|
from .numpy_client import NumPyClient
|
|
50
|
+
from .supernode.app import parse_args_run_client_app
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
def run_client_app() -> None:
|
|
53
54
|
"""Run Flower client app."""
|
|
54
|
-
event(EventType.RUN_CLIENT_APP_ENTER)
|
|
55
|
-
|
|
56
55
|
log(INFO, "Long-running Flower client starting")
|
|
57
56
|
|
|
57
|
+
event(EventType.RUN_CLIENT_APP_ENTER)
|
|
58
|
+
|
|
58
59
|
args = _parse_args_run_client_app().parse_args()
|
|
59
60
|
|
|
60
61
|
# Obtain certificates
|
|
@@ -131,56 +132,7 @@ def _parse_args_run_client_app() -> argparse.ArgumentParser:
|
|
|
131
132
|
description="Start a Flower client app",
|
|
132
133
|
)
|
|
133
134
|
|
|
134
|
-
parser
|
|
135
|
-
"client-app",
|
|
136
|
-
help="For example: `client:app` or `project.package.module:wrapper.app`",
|
|
137
|
-
)
|
|
138
|
-
parser.add_argument(
|
|
139
|
-
"--insecure",
|
|
140
|
-
action="store_true",
|
|
141
|
-
help="Run the client without HTTPS. By default, the client runs with "
|
|
142
|
-
"HTTPS enabled. Use this flag only if you understand the risks.",
|
|
143
|
-
)
|
|
144
|
-
parser.add_argument(
|
|
145
|
-
"--rest",
|
|
146
|
-
action="store_true",
|
|
147
|
-
help="Use REST as a transport layer for the client.",
|
|
148
|
-
)
|
|
149
|
-
parser.add_argument(
|
|
150
|
-
"--root-certificates",
|
|
151
|
-
metavar="ROOT_CERT",
|
|
152
|
-
type=str,
|
|
153
|
-
help="Specifies the path to the PEM-encoded root certificate file for "
|
|
154
|
-
"establishing secure HTTPS connections.",
|
|
155
|
-
)
|
|
156
|
-
parser.add_argument(
|
|
157
|
-
"--server",
|
|
158
|
-
default="0.0.0.0:9092",
|
|
159
|
-
help="Server address",
|
|
160
|
-
)
|
|
161
|
-
parser.add_argument(
|
|
162
|
-
"--max-retries",
|
|
163
|
-
type=int,
|
|
164
|
-
default=None,
|
|
165
|
-
help="The maximum number of times the client will try to connect to the"
|
|
166
|
-
"server before giving up in case of a connection error. By default,"
|
|
167
|
-
"it is set to None, meaning there is no limit to the number of tries.",
|
|
168
|
-
)
|
|
169
|
-
parser.add_argument(
|
|
170
|
-
"--max-wait-time",
|
|
171
|
-
type=float,
|
|
172
|
-
default=None,
|
|
173
|
-
help="The maximum duration before the client stops trying to"
|
|
174
|
-
"connect to the server in case of connection error. By default, it"
|
|
175
|
-
"is set to None, meaning there is no limit to the total time.",
|
|
176
|
-
)
|
|
177
|
-
parser.add_argument(
|
|
178
|
-
"--dir",
|
|
179
|
-
default="",
|
|
180
|
-
help="Add specified directory to the PYTHONPATH and load Flower "
|
|
181
|
-
"app from there."
|
|
182
|
-
" Default: current working directory.",
|
|
183
|
-
)
|
|
135
|
+
parse_args_run_client_app(parser=parser)
|
|
184
136
|
|
|
185
137
|
return parser
|
|
186
138
|
|
|
@@ -442,7 +394,8 @@ def _start_client_internal(
|
|
|
442
394
|
grpc_max_message_length,
|
|
443
395
|
root_certificates,
|
|
444
396
|
) as conn:
|
|
445
|
-
|
|
397
|
+
# pylint: disable-next=W0612
|
|
398
|
+
receive, send, create_node, delete_node, get_run = conn
|
|
446
399
|
|
|
447
400
|
# Register node
|
|
448
401
|
if create_node is not None:
|
|
@@ -660,6 +613,7 @@ def _init_connection(transport: Optional[str], server_address: str) -> Tuple[
|
|
|
660
613
|
Callable[[Message], None],
|
|
661
614
|
Optional[Callable[[], None]],
|
|
662
615
|
Optional[Callable[[], None]],
|
|
616
|
+
Optional[Callable[[int], Tuple[str, str]]],
|
|
663
617
|
]
|
|
664
618
|
],
|
|
665
619
|
],
|
|
@@ -68,6 +68,7 @@ def grpc_connection( # pylint: disable=R0915
|
|
|
68
68
|
Callable[[Message], None],
|
|
69
69
|
Optional[Callable[[], None]],
|
|
70
70
|
Optional[Callable[[], None]],
|
|
71
|
+
Optional[Callable[[int], Tuple[str, str]]],
|
|
71
72
|
]
|
|
72
73
|
]:
|
|
73
74
|
"""Establish a gRPC connection to a gRPC server.
|
|
@@ -224,7 +225,7 @@ def grpc_connection( # pylint: disable=R0915
|
|
|
224
225
|
|
|
225
226
|
try:
|
|
226
227
|
# Yield methods
|
|
227
|
-
yield (receive, send, None, None)
|
|
228
|
+
yield (receive, send, None, None, None)
|
|
228
229
|
finally:
|
|
229
230
|
# Make sure to have a final
|
|
230
231
|
channel.close()
|
|
@@ -41,6 +41,8 @@ from flwr.common.serde import message_from_taskins, message_to_taskres
|
|
|
41
41
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
42
42
|
CreateNodeRequest,
|
|
43
43
|
DeleteNodeRequest,
|
|
44
|
+
GetRunRequest,
|
|
45
|
+
GetRunResponse,
|
|
44
46
|
PingRequest,
|
|
45
47
|
PingResponse,
|
|
46
48
|
PullTaskInsRequest,
|
|
@@ -69,6 +71,7 @@ def grpc_request_response( # pylint: disable=R0914, R0915
|
|
|
69
71
|
Callable[[Message], None],
|
|
70
72
|
Optional[Callable[[], None]],
|
|
71
73
|
Optional[Callable[[], None]],
|
|
74
|
+
Optional[Callable[[int], Tuple[str, str]]],
|
|
72
75
|
]
|
|
73
76
|
]:
|
|
74
77
|
"""Primitives for request/response-based interaction with a server.
|
|
@@ -122,7 +125,7 @@ def grpc_request_response( # pylint: disable=R0914, R0915
|
|
|
122
125
|
ping_stop_event = threading.Event()
|
|
123
126
|
|
|
124
127
|
###########################################################################
|
|
125
|
-
# ping/create_node/delete_node/receive/send functions
|
|
128
|
+
# ping/create_node/delete_node/receive/send/get_run functions
|
|
126
129
|
###########################################################################
|
|
127
130
|
|
|
128
131
|
def ping() -> None:
|
|
@@ -241,8 +244,19 @@ def grpc_request_response( # pylint: disable=R0914, R0915
|
|
|
241
244
|
# Cleanup
|
|
242
245
|
metadata = None
|
|
243
246
|
|
|
247
|
+
def get_run(run_id: int) -> Tuple[str, str]:
|
|
248
|
+
# Call FleetAPI
|
|
249
|
+
get_run_request = GetRunRequest(run_id=run_id)
|
|
250
|
+
get_run_response: GetRunResponse = retry_invoker.invoke(
|
|
251
|
+
stub.GetRun,
|
|
252
|
+
request=get_run_request,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Return fab_id and fab_version
|
|
256
|
+
return get_run_response.run.fab_id, get_run_response.run.fab_version
|
|
257
|
+
|
|
244
258
|
try:
|
|
245
259
|
# Yield methods
|
|
246
|
-
yield (receive, send, create_node, delete_node)
|
|
260
|
+
yield (receive, send, create_node, delete_node, get_run)
|
|
247
261
|
except Exception as exc: # pylint: disable=broad-except
|
|
248
262
|
log(ERROR, exc)
|
|
@@ -21,7 +21,9 @@ import threading
|
|
|
21
21
|
from contextlib import contextmanager
|
|
22
22
|
from copy import copy
|
|
23
23
|
from logging import ERROR, INFO, WARN
|
|
24
|
-
from typing import Callable, Iterator, Optional, Tuple, Union
|
|
24
|
+
from typing import Callable, Iterator, Optional, Tuple, Type, TypeVar, Union
|
|
25
|
+
|
|
26
|
+
from google.protobuf.message import Message as GrpcMessage
|
|
25
27
|
|
|
26
28
|
from flwr.client.heartbeat import start_ping_loop
|
|
27
29
|
from flwr.client.message_handler.message_handler import validate_out_message
|
|
@@ -42,6 +44,9 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
|
42
44
|
CreateNodeRequest,
|
|
43
45
|
CreateNodeResponse,
|
|
44
46
|
DeleteNodeRequest,
|
|
47
|
+
DeleteNodeResponse,
|
|
48
|
+
GetRunRequest,
|
|
49
|
+
GetRunResponse,
|
|
45
50
|
PingRequest,
|
|
46
51
|
PingResponse,
|
|
47
52
|
PullTaskInsRequest,
|
|
@@ -63,6 +68,9 @@ PATH_DELETE_NODE: str = "api/v0/fleet/delete-node"
|
|
|
63
68
|
PATH_PULL_TASK_INS: str = "api/v0/fleet/pull-task-ins"
|
|
64
69
|
PATH_PUSH_TASK_RES: str = "api/v0/fleet/push-task-res"
|
|
65
70
|
PATH_PING: str = "api/v0/fleet/ping"
|
|
71
|
+
PATH_GET_RUN: str = "/api/v0/fleet/get-run"
|
|
72
|
+
|
|
73
|
+
T = TypeVar("T", bound=GrpcMessage)
|
|
66
74
|
|
|
67
75
|
|
|
68
76
|
@contextmanager
|
|
@@ -80,6 +88,7 @@ def http_request_response( # pylint: disable=R0914, R0915
|
|
|
80
88
|
Callable[[Message], None],
|
|
81
89
|
Optional[Callable[[], None]],
|
|
82
90
|
Optional[Callable[[], None]],
|
|
91
|
+
Optional[Callable[[int], Tuple[str, str]]],
|
|
83
92
|
]
|
|
84
93
|
]:
|
|
85
94
|
"""Primitives for request/response-based interaction with a server.
|
|
@@ -141,55 +150,72 @@ def http_request_response( # pylint: disable=R0914, R0915
|
|
|
141
150
|
ping_stop_event = threading.Event()
|
|
142
151
|
|
|
143
152
|
###########################################################################
|
|
144
|
-
# ping/create_node/delete_node/receive/send functions
|
|
153
|
+
# ping/create_node/delete_node/receive/send/get_run functions
|
|
145
154
|
###########################################################################
|
|
146
155
|
|
|
147
|
-
def
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
# Construct the ping request
|
|
154
|
-
req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
|
|
155
|
-
req_bytes: bytes = req.SerializeToString()
|
|
156
|
+
def _request(
|
|
157
|
+
req: GrpcMessage, res_type: Type[T], api_path: str, retry: bool = True
|
|
158
|
+
) -> Optional[T]:
|
|
159
|
+
# Serialize the request
|
|
160
|
+
req_bytes = req.SerializeToString()
|
|
156
161
|
|
|
157
162
|
# Send the request
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
163
|
+
def post() -> requests.Response:
|
|
164
|
+
return requests.post(
|
|
165
|
+
f"{base_url}/{api_path}",
|
|
166
|
+
data=req_bytes,
|
|
167
|
+
headers={
|
|
168
|
+
"Accept": "application/protobuf",
|
|
169
|
+
"Content-Type": "application/protobuf",
|
|
170
|
+
},
|
|
171
|
+
verify=verify,
|
|
172
|
+
timeout=None,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if retry:
|
|
176
|
+
res: requests.Response = retry_invoker.invoke(post)
|
|
177
|
+
else:
|
|
178
|
+
res = post()
|
|
168
179
|
|
|
169
180
|
# Check status code and headers
|
|
170
181
|
if res.status_code != 200:
|
|
171
|
-
return
|
|
182
|
+
return None
|
|
172
183
|
if "content-type" not in res.headers:
|
|
173
184
|
log(
|
|
174
185
|
WARN,
|
|
175
186
|
"[Node] POST /%s: missing header `Content-Type`",
|
|
176
|
-
|
|
187
|
+
api_path,
|
|
177
188
|
)
|
|
178
|
-
return
|
|
189
|
+
return None
|
|
179
190
|
if res.headers["content-type"] != "application/protobuf":
|
|
180
191
|
log(
|
|
181
192
|
WARN,
|
|
182
193
|
"[Node] POST /%s: header `Content-Type` has wrong value",
|
|
183
|
-
|
|
194
|
+
api_path,
|
|
184
195
|
)
|
|
185
|
-
return
|
|
196
|
+
return None
|
|
186
197
|
|
|
187
198
|
# Deserialize ProtoBuf from bytes
|
|
188
|
-
|
|
189
|
-
|
|
199
|
+
grpc_res = res_type()
|
|
200
|
+
grpc_res.ParseFromString(res.content)
|
|
201
|
+
return grpc_res
|
|
202
|
+
|
|
203
|
+
def ping() -> None:
|
|
204
|
+
# Get Node
|
|
205
|
+
if node is None:
|
|
206
|
+
log(ERROR, "Node instance missing")
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
# Construct the ping request
|
|
210
|
+
req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
|
|
211
|
+
|
|
212
|
+
# Send the request
|
|
213
|
+
res = _request(req, PingResponse, PATH_PING, retry=False)
|
|
214
|
+
if res is None:
|
|
215
|
+
return
|
|
190
216
|
|
|
191
217
|
# Check if success
|
|
192
|
-
if not
|
|
218
|
+
if not res.success:
|
|
193
219
|
raise RuntimeError("Ping failed unexpectedly.")
|
|
194
220
|
|
|
195
221
|
# Wait
|
|
@@ -201,46 +227,16 @@ def http_request_response( # pylint: disable=R0914, R0915
|
|
|
201
227
|
|
|
202
228
|
def create_node() -> None:
|
|
203
229
|
"""Set create_node."""
|
|
204
|
-
|
|
205
|
-
create_node_req_bytes: bytes = create_node_req_proto.SerializeToString()
|
|
206
|
-
|
|
207
|
-
res = retry_invoker.invoke(
|
|
208
|
-
requests.post,
|
|
209
|
-
url=f"{base_url}/{PATH_CREATE_NODE}",
|
|
210
|
-
headers={
|
|
211
|
-
"Accept": "application/protobuf",
|
|
212
|
-
"Content-Type": "application/protobuf",
|
|
213
|
-
},
|
|
214
|
-
data=create_node_req_bytes,
|
|
215
|
-
verify=verify,
|
|
216
|
-
timeout=None,
|
|
217
|
-
)
|
|
230
|
+
req = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
|
|
218
231
|
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if "content-type" not in res.headers:
|
|
223
|
-
log(
|
|
224
|
-
WARN,
|
|
225
|
-
"[Node] POST /%s: missing header `Content-Type`",
|
|
226
|
-
PATH_CREATE_NODE,
|
|
227
|
-
)
|
|
228
|
-
return
|
|
229
|
-
if res.headers["content-type"] != "application/protobuf":
|
|
230
|
-
log(
|
|
231
|
-
WARN,
|
|
232
|
-
"[Node] POST /%s: header `Content-Type` has wrong value",
|
|
233
|
-
PATH_CREATE_NODE,
|
|
234
|
-
)
|
|
232
|
+
# Send the request
|
|
233
|
+
res = _request(req, CreateNodeResponse, PATH_CREATE_NODE)
|
|
234
|
+
if res is None:
|
|
235
235
|
return
|
|
236
236
|
|
|
237
|
-
# Deserialize ProtoBuf from bytes
|
|
238
|
-
create_node_response_proto = CreateNodeResponse()
|
|
239
|
-
create_node_response_proto.ParseFromString(res.content)
|
|
240
|
-
|
|
241
237
|
# Remember the node and the ping-loop thread
|
|
242
238
|
nonlocal node, ping_thread
|
|
243
|
-
node =
|
|
239
|
+
node = res.node
|
|
244
240
|
ping_thread = start_ping_loop(ping, ping_stop_event)
|
|
245
241
|
|
|
246
242
|
def delete_node() -> None:
|
|
@@ -256,36 +252,12 @@ def http_request_response( # pylint: disable=R0914, R0915
|
|
|
256
252
|
ping_thread.join()
|
|
257
253
|
|
|
258
254
|
# Send DeleteNode request
|
|
259
|
-
|
|
260
|
-
delete_node_req_req_bytes: bytes = delete_node_req_proto.SerializeToString()
|
|
261
|
-
res = retry_invoker.invoke(
|
|
262
|
-
requests.post,
|
|
263
|
-
url=f"{base_url}/{PATH_DELETE_NODE}",
|
|
264
|
-
headers={
|
|
265
|
-
"Accept": "application/protobuf",
|
|
266
|
-
"Content-Type": "application/protobuf",
|
|
267
|
-
},
|
|
268
|
-
data=delete_node_req_req_bytes,
|
|
269
|
-
verify=verify,
|
|
270
|
-
timeout=None,
|
|
271
|
-
)
|
|
255
|
+
req = DeleteNodeRequest(node=node)
|
|
272
256
|
|
|
273
|
-
#
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if "content-type" not in res.headers:
|
|
277
|
-
log(
|
|
278
|
-
WARN,
|
|
279
|
-
"[Node] POST /%s: missing header `Content-Type`",
|
|
280
|
-
PATH_DELETE_NODE,
|
|
281
|
-
)
|
|
257
|
+
# Send the request
|
|
258
|
+
res = _request(req, DeleteNodeResponse, PATH_CREATE_NODE)
|
|
259
|
+
if res is None:
|
|
282
260
|
return
|
|
283
|
-
if res.headers["content-type"] != "application/protobuf":
|
|
284
|
-
log(
|
|
285
|
-
WARN,
|
|
286
|
-
"[Node] POST /%s: header `Content-Type` has wrong value",
|
|
287
|
-
PATH_DELETE_NODE,
|
|
288
|
-
)
|
|
289
261
|
|
|
290
262
|
# Cleanup
|
|
291
263
|
node = None
|
|
@@ -298,46 +270,15 @@ def http_request_response( # pylint: disable=R0914, R0915
|
|
|
298
270
|
return None
|
|
299
271
|
|
|
300
272
|
# Request instructions (task) from server
|
|
301
|
-
|
|
302
|
-
pull_task_ins_req_bytes: bytes = pull_task_ins_req_proto.SerializeToString()
|
|
273
|
+
req = PullTaskInsRequest(node=node)
|
|
303
274
|
|
|
304
|
-
#
|
|
305
|
-
res =
|
|
306
|
-
|
|
307
|
-
url=f"{base_url}/{PATH_PULL_TASK_INS}",
|
|
308
|
-
headers={
|
|
309
|
-
"Accept": "application/protobuf",
|
|
310
|
-
"Content-Type": "application/protobuf",
|
|
311
|
-
},
|
|
312
|
-
data=pull_task_ins_req_bytes,
|
|
313
|
-
verify=verify,
|
|
314
|
-
timeout=None,
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
# Check status code and headers
|
|
318
|
-
if res.status_code != 200:
|
|
319
|
-
return None
|
|
320
|
-
if "content-type" not in res.headers:
|
|
321
|
-
log(
|
|
322
|
-
WARN,
|
|
323
|
-
"[Node] POST /%s: missing header `Content-Type`",
|
|
324
|
-
PATH_PULL_TASK_INS,
|
|
325
|
-
)
|
|
326
|
-
return None
|
|
327
|
-
if res.headers["content-type"] != "application/protobuf":
|
|
328
|
-
log(
|
|
329
|
-
WARN,
|
|
330
|
-
"[Node] POST /%s: header `Content-Type` has wrong value",
|
|
331
|
-
PATH_PULL_TASK_INS,
|
|
332
|
-
)
|
|
275
|
+
# Send the request
|
|
276
|
+
res = _request(req, PullTaskInsResponse, PATH_PULL_TASK_INS)
|
|
277
|
+
if res is None:
|
|
333
278
|
return None
|
|
334
279
|
|
|
335
|
-
# Deserialize ProtoBuf from bytes
|
|
336
|
-
pull_task_ins_response_proto = PullTaskInsResponse()
|
|
337
|
-
pull_task_ins_response_proto.ParseFromString(res.content)
|
|
338
|
-
|
|
339
280
|
# Get the current TaskIns
|
|
340
|
-
task_ins: Optional[TaskIns] = get_task_ins(
|
|
281
|
+
task_ins: Optional[TaskIns] = get_task_ins(res)
|
|
341
282
|
|
|
342
283
|
# Discard the current TaskIns if not valid
|
|
343
284
|
if task_ins is not None and not (
|
|
@@ -372,61 +313,39 @@ def http_request_response( # pylint: disable=R0914, R0915
|
|
|
372
313
|
if not validate_out_message(message, metadata):
|
|
373
314
|
log(ERROR, "Invalid out message")
|
|
374
315
|
return
|
|
316
|
+
metadata = None
|
|
375
317
|
|
|
376
318
|
# Construct TaskRes
|
|
377
319
|
task_res = message_to_taskres(message)
|
|
378
320
|
|
|
379
321
|
# Serialize ProtoBuf to bytes
|
|
380
|
-
|
|
381
|
-
push_task_res_request_bytes: bytes = (
|
|
382
|
-
push_task_res_request_proto.SerializeToString()
|
|
383
|
-
)
|
|
384
|
-
|
|
385
|
-
# Send ClientMessage to server
|
|
386
|
-
res = retry_invoker.invoke(
|
|
387
|
-
requests.post,
|
|
388
|
-
url=f"{base_url}/{PATH_PUSH_TASK_RES}",
|
|
389
|
-
headers={
|
|
390
|
-
"Accept": "application/protobuf",
|
|
391
|
-
"Content-Type": "application/protobuf",
|
|
392
|
-
},
|
|
393
|
-
data=push_task_res_request_bytes,
|
|
394
|
-
verify=verify,
|
|
395
|
-
timeout=None,
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
metadata = None
|
|
322
|
+
req = PushTaskResRequest(task_res_list=[task_res])
|
|
399
323
|
|
|
400
|
-
#
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
if "content-type" not in res.headers:
|
|
404
|
-
log(
|
|
405
|
-
WARN,
|
|
406
|
-
"[Node] POST /%s: missing header `Content-Type`",
|
|
407
|
-
PATH_PUSH_TASK_RES,
|
|
408
|
-
)
|
|
409
|
-
return
|
|
410
|
-
if res.headers["content-type"] != "application/protobuf":
|
|
411
|
-
log(
|
|
412
|
-
WARN,
|
|
413
|
-
"[Node] POST /%s: header `Content-Type` has wrong value",
|
|
414
|
-
PATH_PUSH_TASK_RES,
|
|
415
|
-
)
|
|
324
|
+
# Send the request
|
|
325
|
+
res = _request(req, PushTaskResResponse, PATH_PUSH_TASK_RES)
|
|
326
|
+
if res is None:
|
|
416
327
|
return
|
|
417
328
|
|
|
418
|
-
# Deserialize ProtoBuf from bytes
|
|
419
|
-
push_task_res_response_proto = PushTaskResResponse()
|
|
420
|
-
push_task_res_response_proto.ParseFromString(res.content)
|
|
421
329
|
log(
|
|
422
330
|
INFO,
|
|
423
331
|
"[Node] POST /%s: success, created result %s",
|
|
424
332
|
PATH_PUSH_TASK_RES,
|
|
425
|
-
|
|
333
|
+
res.results, # pylint: disable=no-member
|
|
426
334
|
)
|
|
427
335
|
|
|
336
|
+
def get_run(run_id: int) -> Tuple[str, str]:
|
|
337
|
+
# Construct the request
|
|
338
|
+
req = GetRunRequest(run_id=run_id)
|
|
339
|
+
|
|
340
|
+
# Send the request
|
|
341
|
+
res = _request(req, GetRunResponse, PATH_GET_RUN)
|
|
342
|
+
if res is None:
|
|
343
|
+
return "", ""
|
|
344
|
+
|
|
345
|
+
return res.run.fab_id, res.run.fab_version
|
|
346
|
+
|
|
428
347
|
try:
|
|
429
348
|
# Yield methods
|
|
430
|
-
yield (receive, send, create_node, delete_node)
|
|
349
|
+
yield (receive, send, create_node, delete_node, get_run)
|
|
431
350
|
except Exception as exc: # pylint: disable=broad-except
|
|
432
351
|
log(ERROR, exc)
|
|
@@ -0,0 +1,22 @@
|
|
|
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 SuperNode."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
from .app import run_supernode as run_supernode
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"run_supernode",
|
|
22
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
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 SuperNode."""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
from logging import DEBUG, INFO
|
|
19
|
+
|
|
20
|
+
from flwr.common import EventType, event
|
|
21
|
+
from flwr.common.exit_handlers import register_exit_handlers
|
|
22
|
+
from flwr.common.logger import log
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def run_supernode() -> None:
|
|
26
|
+
"""Run Flower SuperNode."""
|
|
27
|
+
log(INFO, "Starting Flower SuperNode")
|
|
28
|
+
|
|
29
|
+
event(EventType.RUN_SUPERNODE_ENTER)
|
|
30
|
+
|
|
31
|
+
args = _parse_args_run_supernode().parse_args()
|
|
32
|
+
|
|
33
|
+
log(
|
|
34
|
+
DEBUG,
|
|
35
|
+
"Flower will load ClientApp `%s`",
|
|
36
|
+
getattr(args, "client-app"),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Graceful shutdown
|
|
40
|
+
register_exit_handlers(
|
|
41
|
+
event_type=EventType.RUN_SUPERNODE_LEAVE,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _parse_args_run_supernode() -> argparse.ArgumentParser:
|
|
46
|
+
"""Parse flower-supernode command line arguments."""
|
|
47
|
+
parser = argparse.ArgumentParser(
|
|
48
|
+
description="Start a Flower SuperNode",
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
parse_args_run_client_app(parser=parser)
|
|
52
|
+
|
|
53
|
+
return parser
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def parse_args_run_client_app(parser: argparse.ArgumentParser) -> None:
|
|
57
|
+
"""Parse command line arguments."""
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"client-app",
|
|
60
|
+
help="For example: `client:app` or `project.package.module:wrapper.app`",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--insecure",
|
|
64
|
+
action="store_true",
|
|
65
|
+
help="Run the client without HTTPS. By default, the client runs with "
|
|
66
|
+
"HTTPS enabled. Use this flag only if you understand the risks.",
|
|
67
|
+
)
|
|
68
|
+
parser.add_argument(
|
|
69
|
+
"--rest",
|
|
70
|
+
action="store_true",
|
|
71
|
+
help="Use REST as a transport layer for the client.",
|
|
72
|
+
)
|
|
73
|
+
parser.add_argument(
|
|
74
|
+
"--root-certificates",
|
|
75
|
+
metavar="ROOT_CERT",
|
|
76
|
+
type=str,
|
|
77
|
+
help="Specifies the path to the PEM-encoded root certificate file for "
|
|
78
|
+
"establishing secure HTTPS connections.",
|
|
79
|
+
)
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"--server",
|
|
82
|
+
default="0.0.0.0:9092",
|
|
83
|
+
help="Server address",
|
|
84
|
+
)
|
|
85
|
+
parser.add_argument(
|
|
86
|
+
"--max-retries",
|
|
87
|
+
type=int,
|
|
88
|
+
default=None,
|
|
89
|
+
help="The maximum number of times the client will try to connect to the"
|
|
90
|
+
"server before giving up in case of a connection error. By default,"
|
|
91
|
+
"it is set to None, meaning there is no limit to the number of tries.",
|
|
92
|
+
)
|
|
93
|
+
parser.add_argument(
|
|
94
|
+
"--max-wait-time",
|
|
95
|
+
type=float,
|
|
96
|
+
default=None,
|
|
97
|
+
help="The maximum duration before the client stops trying to"
|
|
98
|
+
"connect to the server in case of connection error. By default, it"
|
|
99
|
+
"is set to None, meaning there is no limit to the total time.",
|
|
100
|
+
)
|
|
101
|
+
parser.add_argument(
|
|
102
|
+
"--dir",
|
|
103
|
+
default="",
|
|
104
|
+
help="Add specified directory to the PYTHONPATH and load Flower "
|
|
105
|
+
"app from there."
|
|
106
|
+
" Default: current working directory.",
|
|
107
|
+
)
|
flwr/common/telemetry.py
CHANGED
|
@@ -160,6 +160,10 @@ class EventType(str, Enum):
|
|
|
160
160
|
RUN_SERVER_APP_ENTER = auto()
|
|
161
161
|
RUN_SERVER_APP_LEAVE = auto()
|
|
162
162
|
|
|
163
|
+
# SuperNode
|
|
164
|
+
RUN_SUPERNODE_ENTER = auto()
|
|
165
|
+
RUN_SUPERNODE_LEAVE = auto()
|
|
166
|
+
|
|
163
167
|
|
|
164
168
|
# Use the ThreadPoolExecutor with max_workers=1 to have a queue
|
|
165
169
|
# and also ensure that telemetry calls are not blocking.
|
flwr/server/app.py
CHANGED
|
@@ -291,9 +291,11 @@ def run_fleet_api() -> None:
|
|
|
291
291
|
|
|
292
292
|
# pylint: disable=too-many-branches, too-many-locals, too-many-statements
|
|
293
293
|
def run_superlink() -> None:
|
|
294
|
-
"""Run Flower
|
|
295
|
-
log(INFO, "Starting Flower
|
|
294
|
+
"""Run Flower SuperLink (Driver API and Fleet API)."""
|
|
295
|
+
log(INFO, "Starting Flower SuperLink")
|
|
296
|
+
|
|
296
297
|
event(EventType.RUN_SUPERLINK_ENTER)
|
|
298
|
+
|
|
297
299
|
args = _parse_args_run_superlink().parse_args()
|
|
298
300
|
|
|
299
301
|
# Parse IP address
|
|
@@ -568,9 +570,7 @@ def _parse_args_run_fleet_api() -> argparse.ArgumentParser:
|
|
|
568
570
|
def _parse_args_run_superlink() -> argparse.ArgumentParser:
|
|
569
571
|
"""Parse command line arguments for both Driver API and Fleet API."""
|
|
570
572
|
parser = argparse.ArgumentParser(
|
|
571
|
-
description="
|
|
572
|
-
"(meaning, a Driver API and a Fleet API), "
|
|
573
|
-
"that clients will be able to connect to.",
|
|
573
|
+
description="Start a Flower SuperLink",
|
|
574
574
|
)
|
|
575
575
|
|
|
576
576
|
_add_args_common(parser=parser)
|
|
@@ -33,6 +33,7 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
|
33
33
|
PushTaskResRequest,
|
|
34
34
|
PushTaskResResponse,
|
|
35
35
|
Reconnect,
|
|
36
|
+
Run,
|
|
36
37
|
)
|
|
37
38
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
38
39
|
from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
|
|
@@ -109,4 +110,6 @@ def get_run(
|
|
|
109
110
|
request: GetRunRequest, state: State # pylint: disable=W0613
|
|
110
111
|
) -> GetRunResponse:
|
|
111
112
|
"""Get run information."""
|
|
112
|
-
|
|
113
|
+
run_id, fab_id, fab_version = state.get_run(request.run_id)
|
|
114
|
+
run = Run(run_id=run_id, fab_id=fab_id, fab_version=fab_version)
|
|
115
|
+
return GetRunResponse(run=run)
|
{flwr_nightly-1.9.0.dev20240418.dist-info → flwr_nightly-1.9.0.dev20240419.dist-info}/RECORD
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
flwr/__init__.py,sha256=VmBWedrCxqmt4QvUHBLqyVEH6p7zaFMD_oCHerXHSVw,937
|
|
2
2
|
flwr/cli/__init__.py,sha256=cZJVgozlkC6Ni2Hd_FAIrqefrkCGOV18fikToq-6iLw,720
|
|
3
3
|
flwr/cli/app.py,sha256=38thPnMydBmNAxNE9mz4By-KdRUhJfoUgeDuAxMYF_U,1095
|
|
4
|
-
flwr/cli/config_utils.py,sha256=
|
|
4
|
+
flwr/cli/config_utils.py,sha256=1wTPQqOU2fKeU4FP5KyG0xMa0F-qy8x1m2WvztPORb4,5597
|
|
5
5
|
flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
|
|
6
6
|
flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
|
|
7
7
|
flwr/cli/new/new.py,sha256=OHTOpuHRqmafsoV_Hv1V1544mZz54Z0qDRRtMT3dR-M,5380
|
|
@@ -13,10 +13,10 @@ flwr/cli/new/templates/app/code/__init__.py,sha256=EM6vfvgAILKPaPn7H1wMV1Wi01WyZ
|
|
|
13
13
|
flwr/cli/new/templates/app/code/__init__.py.tpl,sha256=olwrBeJemHNBWvjc6gJURloFRqW40dAy7FRQA5pDqHU,21
|
|
14
14
|
flwr/cli/new/templates/app/code/client.numpy.py.tpl,sha256=mTh7Y_jOJrPUvDYHVJy4wJCnjXZV_q-jlDkB07U5GSk,521
|
|
15
15
|
flwr/cli/new/templates/app/code/client.pytorch.py.tpl,sha256=671daPcdZaC4Z5k-dqmCovfb2_FShGmqfjwaR8y6EC8,1173
|
|
16
|
-
flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=
|
|
16
|
+
flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=N9SbnI65r2K9FHV_wn4JSpmVeyYpD0qEMehbHcGm4t0,1911
|
|
17
17
|
flwr/cli/new/templates/app/code/server.numpy.py.tpl,sha256=fRxrDXV7pB1aDhQUXMBmrCsC1zp0uKwsBxZBx1JzbHA,248
|
|
18
18
|
flwr/cli/new/templates/app/code/server.pytorch.py.tpl,sha256=xtKvUivNMzgOcLSOtnjWouJzIFbXdUQVYMm27uwyJpI,594
|
|
19
|
-
flwr/cli/new/templates/app/code/server.tensorflow.py.tpl,sha256=
|
|
19
|
+
flwr/cli/new/templates/app/code/server.tensorflow.py.tpl,sha256=GUGH8c_6cxgUB9obVJPaA4thxI7OVXsItyfQDsn9E5k,371
|
|
20
20
|
flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=NvajdZN-eTyfdqKK0v2MrvWITXw9BjJ3Ri5c1haPJDs,3684
|
|
21
21
|
flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=0oTH0lY7q-PpRV4HA5woxJ1eWIgZRFcFsHa7-1lULIQ,489
|
|
22
22
|
flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=GYbMAFD90JBRvy8fJbLU7nDITD3sxHv1TncQrg6mjEE,558
|
|
@@ -24,15 +24,15 @@ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=7I8BYtE28cnc7ZiO
|
|
|
24
24
|
flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
|
|
25
25
|
flwr/cli/run/run.py,sha256=qxXgShEXHONx-Gjpl515HF60QzRA-Ygpj2sbl0bZUAA,2331
|
|
26
26
|
flwr/cli/utils.py,sha256=33m5ELefA43VhJwtBHW5ntWkP7X5Tk_5A2s1OcaSBYg,4153
|
|
27
|
-
flwr/client/__init__.py,sha256=
|
|
28
|
-
flwr/client/app.py,sha256=
|
|
27
|
+
flwr/client/__init__.py,sha256=8LuIrd2GGWJXG2CFWihywicJtntIvCoPLssIUnHqZaA,1262
|
|
28
|
+
flwr/client/app.py,sha256=zs5yeFavIIX-407b25xLapVruprohKSB0Ckk0CjW1Vw,24670
|
|
29
29
|
flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
|
|
30
30
|
flwr/client/client_app.py,sha256=-Cs0084tLQUoBCeYZdG2KgU7cjp95_ZJ4MfjoaN4Fzk,8636
|
|
31
31
|
flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
|
|
32
32
|
flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
|
|
33
|
-
flwr/client/grpc_client/connection.py,sha256=
|
|
33
|
+
flwr/client/grpc_client/connection.py,sha256=7MfyR6hEq3u46wK3s0vP3eubFq19pKZJCG3EFw_i4T4,8775
|
|
34
34
|
flwr/client/grpc_rere_client/__init__.py,sha256=avn6W_vHEM_yZEB1S7hCZgnTbXb6ZujqRP_vAzyXu-0,752
|
|
35
|
-
flwr/client/grpc_rere_client/connection.py,sha256=
|
|
35
|
+
flwr/client/grpc_rere_client/connection.py,sha256=IEGkM0MymZ1tyL6yAL4ic5ZpGy_zg9bJBVf5KCSL2iY,9052
|
|
36
36
|
flwr/client/heartbeat.py,sha256=cx37mJBH8LyoIN4Lks85wtqT1mnU5GulQnr4pGCvAq0,2404
|
|
37
37
|
flwr/client/message_handler/__init__.py,sha256=abHvBRJJiiaAMNgeILQbMOa6h8WqMK2BcnvxwQZFpic,719
|
|
38
38
|
flwr/client/message_handler/message_handler.py,sha256=ml_FlduAJ5pxO31n1tKRrWfQRSxkMgKLbwXXcRsNSos,6553
|
|
@@ -49,7 +49,9 @@ flwr/client/node_state.py,sha256=KTTs_l4I0jBM7IsSsbAGjhfL_yZC3QANbzyvyfZBRDM,177
|
|
|
49
49
|
flwr/client/node_state_tests.py,sha256=gPwz0zf2iuDSa11jedkur_u3Xm7lokIDG5ALD2MCvSw,2195
|
|
50
50
|
flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,10283
|
|
51
51
|
flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
|
|
52
|
-
flwr/client/rest_client/connection.py,sha256=
|
|
52
|
+
flwr/client/rest_client/connection.py,sha256=ZxTFVDXlONqKTX6uYgxshoEWqzqVcQ8QQ2hKS93oLM8,11302
|
|
53
|
+
flwr/client/supernode/__init__.py,sha256=D5swXxemuRbA2rB_T9B8LwJW-_PucXwmlFQQerwIUv0,793
|
|
54
|
+
flwr/client/supernode/app.py,sha256=JXRZ76JdyAkhfaEEqsMiONWVQ0bn8YqzZg9oHC4Qfko,3436
|
|
53
55
|
flwr/client/typing.py,sha256=c9EvjlEjasxn1Wqx6bGl6Xg6vM1gMFfmXht-E2i5J-k,1006
|
|
54
56
|
flwr/common/__init__.py,sha256=dHOptgKxna78CEQLD5Yu0QIsoSgpIIw5AhIUZCHDWAU,3721
|
|
55
57
|
flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
|
|
@@ -84,7 +86,7 @@ flwr/common/secure_aggregation/quantization.py,sha256=appui7GGrkRPsupF59TkapeV4N
|
|
|
84
86
|
flwr/common/secure_aggregation/secaggplus_constants.py,sha256=Fh7-n6pgL4TUnHpNYXo8iW-n5cOGQgQa-c7RcU80tqQ,2183
|
|
85
87
|
flwr/common/secure_aggregation/secaggplus_utils.py,sha256=87bNZX6CmQekj935R4u3m5hsaEkkfKtGSA-VG2c-O9w,3221
|
|
86
88
|
flwr/common/serde.py,sha256=Yn83kbSf9vJndTa5ldL4DR_bL_wy_bD4lTlD3ZbB658,22250
|
|
87
|
-
flwr/common/telemetry.py,sha256=
|
|
89
|
+
flwr/common/telemetry.py,sha256=Q84hW6l6MCtD8sgQI4sUcp-N-zqAo607jyApeXC5RpM,7865
|
|
88
90
|
flwr/common/typing.py,sha256=3Wu6Ol1Ja6Gb0WdlcXVEn1EHYJbc4oRRJA81vEegxBo,4382
|
|
89
91
|
flwr/common/version.py,sha256=_RDSMGZPEuGKYViZuXPotDtXMvh4iyDH9XOCO4qtPO8,666
|
|
90
92
|
flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
|
|
@@ -118,7 +120,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPc
|
|
|
118
120
|
flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
|
|
119
121
|
flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
120
122
|
flwr/server/__init__.py,sha256=dNLbXIERZ6X9aA_Bit3R9AARwcaZZzEfDuFmEx8VVOE,1785
|
|
121
|
-
flwr/server/app.py,sha256=
|
|
123
|
+
flwr/server/app.py,sha256=FriloRrkDHTlB5G7EBn6sH4v5GhiYFf_ZhbdROgjKbY,24199
|
|
122
124
|
flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
|
|
123
125
|
flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
|
|
124
126
|
flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
|
|
@@ -173,7 +175,7 @@ flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=1QyBX5qcFPjMVlv7Trvn
|
|
|
173
175
|
flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=bEJOMWbSlqkw-y5ZHtEXczhoSlAxErcRYffmTMQAV8M,758
|
|
174
176
|
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=YGn1IPpuX-6NDgaG1UbyREbI9iAyKDimZuNeWxbG6s0,3387
|
|
175
177
|
flwr/server/superlink/fleet/message_handler/__init__.py,sha256=hEY0l61ojH8Iz30_K1btm1HJ6J49iZJSFUsVYqUTw3A,731
|
|
176
|
-
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=
|
|
178
|
+
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=lG3BkiONcikDVowK0An06V7p2SNkwGbWE5hfN2xlsZw,3622
|
|
177
179
|
flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=VKDvDq5H8koOUztpmQacVzGJXPLEEkL1Vmolxt3mvnY,735
|
|
178
180
|
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=8gNziOjBA8ygTzfVPYiNkg_qxr-T822Q_Lbo9g2tVyk,7621
|
|
179
181
|
flwr/server/superlink/fleet/vce/__init__.py,sha256=36MHKiefnJeyjwMQzVUK4m06Ojon3WDcwZGQsAcyVhQ,783
|
|
@@ -204,8 +206,8 @@ flwr/simulation/ray_transport/ray_actor.py,sha256=_wv2eP7qxkCZ-6rMyYWnjLrGPBZRxj
|
|
|
204
206
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=oDu4sEPIOu39vrNi-fqDAe10xtNUXMO49bM2RWfRcyw,6738
|
|
205
207
|
flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
|
|
206
208
|
flwr/simulation/run_simulation.py,sha256=HiIH6aa_v56NfKQN5ZBd94NyVfaZNyFs43_kItYsQXU,15685
|
|
207
|
-
flwr_nightly-1.9.0.
|
|
208
|
-
flwr_nightly-1.9.0.
|
|
209
|
-
flwr_nightly-1.9.0.
|
|
210
|
-
flwr_nightly-1.9.0.
|
|
211
|
-
flwr_nightly-1.9.0.
|
|
209
|
+
flwr_nightly-1.9.0.dev20240419.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
210
|
+
flwr_nightly-1.9.0.dev20240419.dist-info/METADATA,sha256=W3tyRxj4LXms8QbNvSBIspZOouKU5DIz-UZ-UAiOsYw,15260
|
|
211
|
+
flwr_nightly-1.9.0.dev20240419.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
212
|
+
flwr_nightly-1.9.0.dev20240419.dist-info/entry_points.txt,sha256=DBrrf685V2W9NbbchQwvuqBEpj5ik8tMZNoZg_W2bZY,363
|
|
213
|
+
flwr_nightly-1.9.0.dev20240419.dist-info/RECORD,,
|
{flwr_nightly-1.9.0.dev20240418.dist-info → flwr_nightly-1.9.0.dev20240419.dist-info}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|