flwr-nightly 1.8.0.dev20240328__py3-none-any.whl → 1.8.0.dev20240401__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/client/app.py +2 -4
- flwr/client/client_app.py +7 -0
- flwr/client/grpc_rere_client/connection.py +70 -28
- flwr/client/heartbeat.py +72 -0
- flwr/client/rest_client/connection.py +101 -27
- flwr/common/constant.py +6 -0
- flwr/common/logger.py +4 -4
- flwr/common/retry_invoker.py +24 -13
- flwr/server/server_app.py +3 -0
- flwr/server/superlink/fleet/message_handler/message_handler.py +2 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +28 -0
- flwr/server/superlink/fleet/vce/vce_api.py +8 -0
- {flwr_nightly-1.8.0.dev20240328.dist-info → flwr_nightly-1.8.0.dev20240401.dist-info}/METADATA +1 -1
- {flwr_nightly-1.8.0.dev20240328.dist-info → flwr_nightly-1.8.0.dev20240401.dist-info}/RECORD +17 -16
- {flwr_nightly-1.8.0.dev20240328.dist-info → flwr_nightly-1.8.0.dev20240401.dist-info}/LICENSE +0 -0
- {flwr_nightly-1.8.0.dev20240328.dist-info → flwr_nightly-1.8.0.dev20240401.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.8.0.dev20240328.dist-info → flwr_nightly-1.8.0.dev20240401.dist-info}/entry_points.txt +0 -0
flwr/client/app.py
CHANGED
|
@@ -36,7 +36,7 @@ from flwr.common.constant import (
|
|
|
36
36
|
TRANSPORT_TYPES,
|
|
37
37
|
)
|
|
38
38
|
from flwr.common.exit_handlers import register_exit_handlers
|
|
39
|
-
from flwr.common.logger import log, warn_deprecated_feature
|
|
39
|
+
from flwr.common.logger import log, warn_deprecated_feature
|
|
40
40
|
from flwr.common.message import Error
|
|
41
41
|
from flwr.common.object_ref import load_app, validate
|
|
42
42
|
from flwr.common.retry_invoker import RetryInvoker, exponential
|
|
@@ -385,8 +385,6 @@ def _start_client_internal(
|
|
|
385
385
|
return ClientApp(client_fn=client_fn)
|
|
386
386
|
|
|
387
387
|
load_client_app_fn = _load_client_app
|
|
388
|
-
else:
|
|
389
|
-
warn_experimental_feature("`load_client_app_fn`")
|
|
390
388
|
|
|
391
389
|
# At this point, only `load_client_app_fn` should be used
|
|
392
390
|
# Both `client` and `client_fn` must not be used directly
|
|
@@ -397,7 +395,7 @@ def _start_client_internal(
|
|
|
397
395
|
)
|
|
398
396
|
|
|
399
397
|
retry_invoker = RetryInvoker(
|
|
400
|
-
|
|
398
|
+
wait_gen_factory=exponential,
|
|
401
399
|
recoverable_exceptions=connection_error_type,
|
|
402
400
|
max_tries=max_retries,
|
|
403
401
|
max_time=max_wait_time,
|
flwr/client/client_app.py
CHANGED
|
@@ -23,6 +23,7 @@ from flwr.client.message_handler.message_handler import (
|
|
|
23
23
|
from flwr.client.mod.utils import make_ffn
|
|
24
24
|
from flwr.client.typing import ClientFn, Mod
|
|
25
25
|
from flwr.common import Context, Message, MessageType
|
|
26
|
+
from flwr.common.logger import warn_preview_feature
|
|
26
27
|
|
|
27
28
|
from .typing import ClientAppCallable
|
|
28
29
|
|
|
@@ -123,6 +124,8 @@ class ClientApp:
|
|
|
123
124
|
if self._call:
|
|
124
125
|
raise _registration_error(MessageType.TRAIN)
|
|
125
126
|
|
|
127
|
+
warn_preview_feature("ClientApp-register-train-function")
|
|
128
|
+
|
|
126
129
|
# Register provided function with the ClientApp object
|
|
127
130
|
# Wrap mods around the wrapped step function
|
|
128
131
|
self._train = make_ffn(train_fn, self._mods)
|
|
@@ -151,6 +154,8 @@ class ClientApp:
|
|
|
151
154
|
if self._call:
|
|
152
155
|
raise _registration_error(MessageType.EVALUATE)
|
|
153
156
|
|
|
157
|
+
warn_preview_feature("ClientApp-register-evaluate-function")
|
|
158
|
+
|
|
154
159
|
# Register provided function with the ClientApp object
|
|
155
160
|
# Wrap mods around the wrapped step function
|
|
156
161
|
self._evaluate = make_ffn(evaluate_fn, self._mods)
|
|
@@ -179,6 +184,8 @@ class ClientApp:
|
|
|
179
184
|
if self._call:
|
|
180
185
|
raise _registration_error(MessageType.QUERY)
|
|
181
186
|
|
|
187
|
+
warn_preview_feature("ClientApp-register-query-function")
|
|
188
|
+
|
|
182
189
|
# Register provided function with the ClientApp object
|
|
183
190
|
# Wrap mods around the wrapped step function
|
|
184
191
|
self._query = make_ffn(query_fn, self._mods)
|
|
@@ -15,23 +15,34 @@
|
|
|
15
15
|
"""Contextmanager for a gRPC request-response channel to the Flower server."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
import random
|
|
19
|
+
import threading
|
|
18
20
|
from contextlib import contextmanager
|
|
19
21
|
from copy import copy
|
|
20
22
|
from logging import DEBUG, ERROR
|
|
21
23
|
from pathlib import Path
|
|
22
|
-
from typing import Callable,
|
|
24
|
+
from typing import Callable, Iterator, Optional, Tuple, Union, cast
|
|
23
25
|
|
|
26
|
+
from flwr.client.heartbeat import start_ping_loop
|
|
24
27
|
from flwr.client.message_handler.message_handler import validate_out_message
|
|
25
28
|
from flwr.client.message_handler.task_handler import get_task_ins, validate_task_ins
|
|
26
29
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
30
|
+
from flwr.common.constant import (
|
|
31
|
+
PING_BASE_MULTIPLIER,
|
|
32
|
+
PING_CALL_TIMEOUT,
|
|
33
|
+
PING_DEFAULT_INTERVAL,
|
|
34
|
+
PING_RANDOM_RANGE,
|
|
35
|
+
)
|
|
27
36
|
from flwr.common.grpc import create_channel
|
|
28
|
-
from flwr.common.logger import log
|
|
37
|
+
from flwr.common.logger import log
|
|
29
38
|
from flwr.common.message import Message, Metadata
|
|
30
39
|
from flwr.common.retry_invoker import RetryInvoker
|
|
31
40
|
from flwr.common.serde import message_from_taskins, message_to_taskres
|
|
32
41
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
33
42
|
CreateNodeRequest,
|
|
34
43
|
DeleteNodeRequest,
|
|
44
|
+
PingRequest,
|
|
45
|
+
PingResponse,
|
|
35
46
|
PullTaskInsRequest,
|
|
36
47
|
PushTaskResRequest,
|
|
37
48
|
)
|
|
@@ -39,9 +50,6 @@ from flwr.proto.fleet_pb2_grpc import FleetStub # pylint: disable=E0611
|
|
|
39
50
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
40
51
|
from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
|
|
41
52
|
|
|
42
|
-
KEY_NODE = "node"
|
|
43
|
-
KEY_METADATA = "in_message_metadata"
|
|
44
|
-
|
|
45
53
|
|
|
46
54
|
def on_channel_state_change(channel_connectivity: str) -> None:
|
|
47
55
|
"""Log channel connectivity."""
|
|
@@ -49,7 +57,7 @@ def on_channel_state_change(channel_connectivity: str) -> None:
|
|
|
49
57
|
|
|
50
58
|
|
|
51
59
|
@contextmanager
|
|
52
|
-
def grpc_request_response(
|
|
60
|
+
def grpc_request_response( # pylint: disable=R0914, R0915
|
|
53
61
|
server_address: str,
|
|
54
62
|
insecure: bool,
|
|
55
63
|
retry_invoker: RetryInvoker,
|
|
@@ -95,8 +103,6 @@ def grpc_request_response(
|
|
|
95
103
|
create_node : Optional[Callable]
|
|
96
104
|
delete_node : Optional[Callable]
|
|
97
105
|
"""
|
|
98
|
-
warn_experimental_feature("`grpc-rere`")
|
|
99
|
-
|
|
100
106
|
if isinstance(root_certificates, str):
|
|
101
107
|
root_certificates = Path(root_certificates).read_bytes()
|
|
102
108
|
|
|
@@ -107,47 +113,81 @@ def grpc_request_response(
|
|
|
107
113
|
max_message_length=max_message_length,
|
|
108
114
|
)
|
|
109
115
|
channel.subscribe(on_channel_state_change)
|
|
110
|
-
stub = FleetStub(channel)
|
|
111
116
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
+
# Shared variables for inner functions
|
|
118
|
+
stub = FleetStub(channel)
|
|
119
|
+
metadata: Optional[Metadata] = None
|
|
120
|
+
node: Optional[Node] = None
|
|
121
|
+
ping_thread: Optional[threading.Thread] = None
|
|
122
|
+
ping_stop_event = threading.Event()
|
|
117
123
|
|
|
118
124
|
###########################################################################
|
|
119
|
-
# receive/send functions
|
|
125
|
+
# ping/create_node/delete_node/receive/send functions
|
|
120
126
|
###########################################################################
|
|
121
127
|
|
|
128
|
+
def ping() -> None:
|
|
129
|
+
# Get Node
|
|
130
|
+
if node is None:
|
|
131
|
+
log(ERROR, "Node instance missing")
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
# Construct the ping request
|
|
135
|
+
req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
|
|
136
|
+
|
|
137
|
+
# Call FleetAPI
|
|
138
|
+
res: PingResponse = stub.Ping(req, timeout=PING_CALL_TIMEOUT)
|
|
139
|
+
|
|
140
|
+
# Check if success
|
|
141
|
+
if not res.success:
|
|
142
|
+
raise RuntimeError("Ping failed unexpectedly.")
|
|
143
|
+
|
|
144
|
+
# Wait
|
|
145
|
+
rd = random.uniform(*PING_RANDOM_RANGE)
|
|
146
|
+
next_interval: float = PING_DEFAULT_INTERVAL - PING_CALL_TIMEOUT
|
|
147
|
+
next_interval *= PING_BASE_MULTIPLIER + rd
|
|
148
|
+
if not ping_stop_event.is_set():
|
|
149
|
+
ping_stop_event.wait(next_interval)
|
|
150
|
+
|
|
122
151
|
def create_node() -> None:
|
|
123
152
|
"""Set create_node."""
|
|
153
|
+
# Call FleetAPI
|
|
124
154
|
create_node_request = CreateNodeRequest()
|
|
125
155
|
create_node_response = retry_invoker.invoke(
|
|
126
156
|
stub.CreateNode,
|
|
127
157
|
request=create_node_request,
|
|
128
158
|
)
|
|
129
|
-
|
|
159
|
+
|
|
160
|
+
# Remember the node and the ping-loop thread
|
|
161
|
+
nonlocal node, ping_thread
|
|
162
|
+
node = cast(Node, create_node_response.node)
|
|
163
|
+
ping_thread = start_ping_loop(ping, ping_stop_event)
|
|
130
164
|
|
|
131
165
|
def delete_node() -> None:
|
|
132
166
|
"""Set delete_node."""
|
|
133
167
|
# Get Node
|
|
134
|
-
|
|
168
|
+
nonlocal node
|
|
169
|
+
if node is None:
|
|
135
170
|
log(ERROR, "Node instance missing")
|
|
136
171
|
return
|
|
137
|
-
node: Node = cast(Node, node_store[KEY_NODE])
|
|
138
172
|
|
|
173
|
+
# Stop the ping-loop thread
|
|
174
|
+
ping_stop_event.set()
|
|
175
|
+
if ping_thread is not None:
|
|
176
|
+
ping_thread.join()
|
|
177
|
+
|
|
178
|
+
# Call FleetAPI
|
|
139
179
|
delete_node_request = DeleteNodeRequest(node=node)
|
|
140
180
|
retry_invoker.invoke(stub.DeleteNode, request=delete_node_request)
|
|
141
181
|
|
|
142
|
-
|
|
182
|
+
# Cleanup
|
|
183
|
+
node = None
|
|
143
184
|
|
|
144
185
|
def receive() -> Optional[Message]:
|
|
145
186
|
"""Receive next task from server."""
|
|
146
187
|
# Get Node
|
|
147
|
-
if
|
|
188
|
+
if node is None:
|
|
148
189
|
log(ERROR, "Node instance missing")
|
|
149
190
|
return None
|
|
150
|
-
node: Node = cast(Node, node_store[KEY_NODE])
|
|
151
191
|
|
|
152
192
|
# Request instructions (task) from server
|
|
153
193
|
request = PullTaskInsRequest(node=node)
|
|
@@ -167,7 +207,8 @@ def grpc_request_response(
|
|
|
167
207
|
in_message = message_from_taskins(task_ins) if task_ins else None
|
|
168
208
|
|
|
169
209
|
# Remember `metadata` of the in message
|
|
170
|
-
|
|
210
|
+
nonlocal metadata
|
|
211
|
+
metadata = copy(in_message.metadata) if in_message else None
|
|
171
212
|
|
|
172
213
|
# Return the message if available
|
|
173
214
|
return in_message
|
|
@@ -175,18 +216,18 @@ def grpc_request_response(
|
|
|
175
216
|
def send(message: Message) -> None:
|
|
176
217
|
"""Send task result back to server."""
|
|
177
218
|
# Get Node
|
|
178
|
-
if
|
|
219
|
+
if node is None:
|
|
179
220
|
log(ERROR, "Node instance missing")
|
|
180
221
|
return
|
|
181
222
|
|
|
182
|
-
# Get incoming message
|
|
183
|
-
|
|
184
|
-
if
|
|
223
|
+
# Get the metadata of the incoming message
|
|
224
|
+
nonlocal metadata
|
|
225
|
+
if metadata is None:
|
|
185
226
|
log(ERROR, "No current message")
|
|
186
227
|
return
|
|
187
228
|
|
|
188
229
|
# Validate out message
|
|
189
|
-
if not validate_out_message(message,
|
|
230
|
+
if not validate_out_message(message, metadata):
|
|
190
231
|
log(ERROR, "Invalid out message")
|
|
191
232
|
return
|
|
192
233
|
|
|
@@ -197,7 +238,8 @@ def grpc_request_response(
|
|
|
197
238
|
request = PushTaskResRequest(task_res_list=[task_res])
|
|
198
239
|
_ = retry_invoker.invoke(stub.PushTaskRes, request)
|
|
199
240
|
|
|
200
|
-
|
|
241
|
+
# Cleanup
|
|
242
|
+
metadata = None
|
|
201
243
|
|
|
202
244
|
try:
|
|
203
245
|
# Yield methods
|
flwr/client/heartbeat.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
"""Heartbeat utility functions."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import threading
|
|
19
|
+
from typing import Callable
|
|
20
|
+
|
|
21
|
+
import grpc
|
|
22
|
+
|
|
23
|
+
from flwr.common.constant import PING_CALL_TIMEOUT
|
|
24
|
+
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _ping_loop(ping_fn: Callable[[], None], stop_event: threading.Event) -> None:
|
|
28
|
+
def wait_fn(wait_time: float) -> None:
|
|
29
|
+
if not stop_event.is_set():
|
|
30
|
+
stop_event.wait(wait_time)
|
|
31
|
+
|
|
32
|
+
def on_backoff(state: RetryState) -> None:
|
|
33
|
+
err = state.exception
|
|
34
|
+
if not isinstance(err, grpc.RpcError):
|
|
35
|
+
return
|
|
36
|
+
status_code = err.code()
|
|
37
|
+
# If ping call timeout is triggered
|
|
38
|
+
if status_code == grpc.StatusCode.DEADLINE_EXCEEDED:
|
|
39
|
+
# Avoid long wait time.
|
|
40
|
+
if state.actual_wait is None:
|
|
41
|
+
return
|
|
42
|
+
state.actual_wait = max(state.actual_wait - PING_CALL_TIMEOUT, 0.0)
|
|
43
|
+
|
|
44
|
+
def wrapped_ping() -> None:
|
|
45
|
+
if not stop_event.is_set():
|
|
46
|
+
ping_fn()
|
|
47
|
+
|
|
48
|
+
retrier = RetryInvoker(
|
|
49
|
+
exponential,
|
|
50
|
+
grpc.RpcError,
|
|
51
|
+
max_tries=None,
|
|
52
|
+
max_time=None,
|
|
53
|
+
on_backoff=on_backoff,
|
|
54
|
+
wait_function=wait_fn,
|
|
55
|
+
)
|
|
56
|
+
while not stop_event.is_set():
|
|
57
|
+
retrier.invoke(wrapped_ping)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def start_ping_loop(
|
|
61
|
+
ping_fn: Callable[[], None], stop_event: threading.Event
|
|
62
|
+
) -> threading.Thread:
|
|
63
|
+
"""Start a ping loop in a separate thread.
|
|
64
|
+
|
|
65
|
+
This function initializes a new thread that runs a ping loop, allowing for
|
|
66
|
+
asynchronous ping operations. The loop can be terminated through the provided stop
|
|
67
|
+
event.
|
|
68
|
+
"""
|
|
69
|
+
thread = threading.Thread(target=_ping_loop, args=(ping_fn, stop_event))
|
|
70
|
+
thread.start()
|
|
71
|
+
|
|
72
|
+
return thread
|
|
@@ -15,16 +15,25 @@
|
|
|
15
15
|
"""Contextmanager for a REST request-response channel to the Flower server."""
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
import random
|
|
18
19
|
import sys
|
|
20
|
+
import threading
|
|
19
21
|
from contextlib import contextmanager
|
|
20
22
|
from copy import copy
|
|
21
23
|
from logging import ERROR, INFO, WARN
|
|
22
|
-
from typing import Callable,
|
|
24
|
+
from typing import Callable, Iterator, Optional, Tuple, Union
|
|
23
25
|
|
|
26
|
+
from flwr.client.heartbeat import start_ping_loop
|
|
24
27
|
from flwr.client.message_handler.message_handler import validate_out_message
|
|
25
28
|
from flwr.client.message_handler.task_handler import get_task_ins, validate_task_ins
|
|
26
29
|
from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
27
|
-
from flwr.common.constant import
|
|
30
|
+
from flwr.common.constant import (
|
|
31
|
+
MISSING_EXTRA_REST,
|
|
32
|
+
PING_BASE_MULTIPLIER,
|
|
33
|
+
PING_CALL_TIMEOUT,
|
|
34
|
+
PING_DEFAULT_INTERVAL,
|
|
35
|
+
PING_RANDOM_RANGE,
|
|
36
|
+
)
|
|
28
37
|
from flwr.common.logger import log
|
|
29
38
|
from flwr.common.message import Message, Metadata
|
|
30
39
|
from flwr.common.retry_invoker import RetryInvoker
|
|
@@ -33,6 +42,8 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
|
33
42
|
CreateNodeRequest,
|
|
34
43
|
CreateNodeResponse,
|
|
35
44
|
DeleteNodeRequest,
|
|
45
|
+
PingRequest,
|
|
46
|
+
PingResponse,
|
|
36
47
|
PullTaskInsRequest,
|
|
37
48
|
PullTaskInsResponse,
|
|
38
49
|
PushTaskResRequest,
|
|
@@ -47,19 +58,15 @@ except ModuleNotFoundError:
|
|
|
47
58
|
sys.exit(MISSING_EXTRA_REST)
|
|
48
59
|
|
|
49
60
|
|
|
50
|
-
KEY_NODE = "node"
|
|
51
|
-
KEY_METADATA = "in_message_metadata"
|
|
52
|
-
|
|
53
|
-
|
|
54
61
|
PATH_CREATE_NODE: str = "api/v0/fleet/create-node"
|
|
55
62
|
PATH_DELETE_NODE: str = "api/v0/fleet/delete-node"
|
|
56
63
|
PATH_PULL_TASK_INS: str = "api/v0/fleet/pull-task-ins"
|
|
57
64
|
PATH_PUSH_TASK_RES: str = "api/v0/fleet/push-task-res"
|
|
65
|
+
PATH_PING: str = "api/v0/fleet/ping"
|
|
58
66
|
|
|
59
67
|
|
|
60
68
|
@contextmanager
|
|
61
|
-
# pylint: disable
|
|
62
|
-
def http_request_response(
|
|
69
|
+
def http_request_response( # pylint: disable=R0914, R0915
|
|
63
70
|
server_address: str,
|
|
64
71
|
insecure: bool, # pylint: disable=unused-argument
|
|
65
72
|
retry_invoker: RetryInvoker,
|
|
@@ -127,16 +134,71 @@ def http_request_response(
|
|
|
127
134
|
"must be provided as a string path to the client.",
|
|
128
135
|
)
|
|
129
136
|
|
|
130
|
-
#
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
# Shared variables for inner functions
|
|
138
|
+
metadata: Optional[Metadata] = None
|
|
139
|
+
node: Optional[Node] = None
|
|
140
|
+
ping_thread: Optional[threading.Thread] = None
|
|
141
|
+
ping_stop_event = threading.Event()
|
|
135
142
|
|
|
136
143
|
###########################################################################
|
|
137
|
-
# receive/send functions
|
|
144
|
+
# ping/create_node/delete_node/receive/send functions
|
|
138
145
|
###########################################################################
|
|
139
146
|
|
|
147
|
+
def ping() -> None:
|
|
148
|
+
# Get Node
|
|
149
|
+
if node is None:
|
|
150
|
+
log(ERROR, "Node instance missing")
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
# Construct the ping request
|
|
154
|
+
req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
|
|
155
|
+
req_bytes: bytes = req.SerializeToString()
|
|
156
|
+
|
|
157
|
+
# Send the request
|
|
158
|
+
res = requests.post(
|
|
159
|
+
url=f"{base_url}/{PATH_PING}",
|
|
160
|
+
headers={
|
|
161
|
+
"Accept": "application/protobuf",
|
|
162
|
+
"Content-Type": "application/protobuf",
|
|
163
|
+
},
|
|
164
|
+
data=req_bytes,
|
|
165
|
+
verify=verify,
|
|
166
|
+
timeout=PING_CALL_TIMEOUT,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Check status code and headers
|
|
170
|
+
if res.status_code != 200:
|
|
171
|
+
return
|
|
172
|
+
if "content-type" not in res.headers:
|
|
173
|
+
log(
|
|
174
|
+
WARN,
|
|
175
|
+
"[Node] POST /%s: missing header `Content-Type`",
|
|
176
|
+
PATH_PULL_TASK_INS,
|
|
177
|
+
)
|
|
178
|
+
return
|
|
179
|
+
if res.headers["content-type"] != "application/protobuf":
|
|
180
|
+
log(
|
|
181
|
+
WARN,
|
|
182
|
+
"[Node] POST /%s: header `Content-Type` has wrong value",
|
|
183
|
+
PATH_PULL_TASK_INS,
|
|
184
|
+
)
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
# Deserialize ProtoBuf from bytes
|
|
188
|
+
ping_res = PingResponse()
|
|
189
|
+
ping_res.ParseFromString(res.content)
|
|
190
|
+
|
|
191
|
+
# Check if success
|
|
192
|
+
if not ping_res.success:
|
|
193
|
+
raise RuntimeError("Ping failed unexpectedly.")
|
|
194
|
+
|
|
195
|
+
# Wait
|
|
196
|
+
rd = random.uniform(*PING_RANDOM_RANGE)
|
|
197
|
+
next_interval: float = PING_DEFAULT_INTERVAL - PING_CALL_TIMEOUT
|
|
198
|
+
next_interval *= PING_BASE_MULTIPLIER + rd
|
|
199
|
+
if not ping_stop_event.is_set():
|
|
200
|
+
ping_stop_event.wait(next_interval)
|
|
201
|
+
|
|
140
202
|
def create_node() -> None:
|
|
141
203
|
"""Set create_node."""
|
|
142
204
|
create_node_req_proto = CreateNodeRequest()
|
|
@@ -175,15 +237,25 @@ def http_request_response(
|
|
|
175
237
|
# Deserialize ProtoBuf from bytes
|
|
176
238
|
create_node_response_proto = CreateNodeResponse()
|
|
177
239
|
create_node_response_proto.ParseFromString(res.content)
|
|
178
|
-
|
|
179
|
-
|
|
240
|
+
|
|
241
|
+
# Remember the node and the ping-loop thread
|
|
242
|
+
nonlocal node, ping_thread
|
|
243
|
+
node = create_node_response_proto.node
|
|
244
|
+
ping_thread = start_ping_loop(ping, ping_stop_event)
|
|
180
245
|
|
|
181
246
|
def delete_node() -> None:
|
|
182
247
|
"""Set delete_node."""
|
|
183
|
-
|
|
248
|
+
nonlocal node
|
|
249
|
+
if node is None:
|
|
184
250
|
log(ERROR, "Node instance missing")
|
|
185
251
|
return
|
|
186
|
-
|
|
252
|
+
|
|
253
|
+
# Stop the ping-loop thread
|
|
254
|
+
ping_stop_event.set()
|
|
255
|
+
if ping_thread is not None:
|
|
256
|
+
ping_thread.join()
|
|
257
|
+
|
|
258
|
+
# Send DeleteNode request
|
|
187
259
|
delete_node_req_proto = DeleteNodeRequest(node=node)
|
|
188
260
|
delete_node_req_req_bytes: bytes = delete_node_req_proto.SerializeToString()
|
|
189
261
|
res = retry_invoker.invoke(
|
|
@@ -215,13 +287,15 @@ def http_request_response(
|
|
|
215
287
|
PATH_PULL_TASK_INS,
|
|
216
288
|
)
|
|
217
289
|
|
|
290
|
+
# Cleanup
|
|
291
|
+
node = None
|
|
292
|
+
|
|
218
293
|
def receive() -> Optional[Message]:
|
|
219
294
|
"""Receive next task from server."""
|
|
220
295
|
# Get Node
|
|
221
|
-
if
|
|
296
|
+
if node is None:
|
|
222
297
|
log(ERROR, "Node instance missing")
|
|
223
298
|
return None
|
|
224
|
-
node: Node = cast(Node, node_store[KEY_NODE])
|
|
225
299
|
|
|
226
300
|
# Request instructions (task) from server
|
|
227
301
|
pull_task_ins_req_proto = PullTaskInsRequest(node=node)
|
|
@@ -273,29 +347,29 @@ def http_request_response(
|
|
|
273
347
|
task_ins = None
|
|
274
348
|
|
|
275
349
|
# Return the Message if available
|
|
350
|
+
nonlocal metadata
|
|
276
351
|
message = None
|
|
277
|
-
state[KEY_METADATA] = None
|
|
278
352
|
if task_ins is not None:
|
|
279
353
|
message = message_from_taskins(task_ins)
|
|
280
|
-
|
|
354
|
+
metadata = copy(message.metadata)
|
|
281
355
|
log(INFO, "[Node] POST /%s: success", PATH_PULL_TASK_INS)
|
|
282
356
|
return message
|
|
283
357
|
|
|
284
358
|
def send(message: Message) -> None:
|
|
285
359
|
"""Send task result back to server."""
|
|
286
360
|
# Get Node
|
|
287
|
-
if
|
|
361
|
+
if node is None:
|
|
288
362
|
log(ERROR, "Node instance missing")
|
|
289
363
|
return
|
|
290
364
|
|
|
291
365
|
# Get incoming message
|
|
292
|
-
|
|
293
|
-
if
|
|
366
|
+
nonlocal metadata
|
|
367
|
+
if metadata is None:
|
|
294
368
|
log(ERROR, "No current message")
|
|
295
369
|
return
|
|
296
370
|
|
|
297
371
|
# Validate out message
|
|
298
|
-
if not validate_out_message(message,
|
|
372
|
+
if not validate_out_message(message, metadata):
|
|
299
373
|
log(ERROR, "Invalid out message")
|
|
300
374
|
return
|
|
301
375
|
|
|
@@ -321,7 +395,7 @@ def http_request_response(
|
|
|
321
395
|
timeout=None,
|
|
322
396
|
)
|
|
323
397
|
|
|
324
|
-
|
|
398
|
+
metadata = None
|
|
325
399
|
|
|
326
400
|
# Check status code and headers
|
|
327
401
|
if res.status_code != 200:
|
flwr/common/constant.py
CHANGED
flwr/common/logger.py
CHANGED
|
@@ -164,13 +164,13 @@ logger = logging.getLogger(LOGGER_NAME) # pylint: disable=invalid-name
|
|
|
164
164
|
log = logger.log # pylint: disable=invalid-name
|
|
165
165
|
|
|
166
166
|
|
|
167
|
-
def
|
|
168
|
-
"""Warn the user when they use
|
|
167
|
+
def warn_preview_feature(name: str) -> None:
|
|
168
|
+
"""Warn the user when they use a preview feature."""
|
|
169
169
|
log(
|
|
170
170
|
WARN,
|
|
171
|
-
"""
|
|
171
|
+
"""PREVIEW FEATURE: %s
|
|
172
172
|
|
|
173
|
-
This is
|
|
173
|
+
This is a preview feature. It could change significantly or be removed
|
|
174
174
|
entirely in future versions of Flower.
|
|
175
175
|
""",
|
|
176
176
|
name,
|
flwr/common/retry_invoker.py
CHANGED
|
@@ -107,7 +107,7 @@ class RetryInvoker:
|
|
|
107
107
|
|
|
108
108
|
Parameters
|
|
109
109
|
----------
|
|
110
|
-
|
|
110
|
+
wait_gen_factory: Callable[[], Generator[float, None, None]]
|
|
111
111
|
A generator yielding successive wait times in seconds. If the generator
|
|
112
112
|
is finite, the giveup event will be triggered when the generator raises
|
|
113
113
|
`StopIteration`.
|
|
@@ -129,12 +129,12 @@ class RetryInvoker:
|
|
|
129
129
|
data class object detailing the invocation.
|
|
130
130
|
on_giveup: Optional[Callable[[RetryState], None]] (default: None)
|
|
131
131
|
A callable to be executed in the event that `max_tries` or `max_time` is
|
|
132
|
-
exceeded, `should_giveup` returns True, or `
|
|
132
|
+
exceeded, `should_giveup` returns True, or `wait_gen_factory()` generator raises
|
|
133
133
|
`StopInteration`. The parameter is a data class object detailing the
|
|
134
134
|
invocation.
|
|
135
135
|
jitter: Optional[Callable[[float], float]] (default: full_jitter)
|
|
136
|
-
A function of the value yielded by `
|
|
137
|
-
to wait. This function helps distribute wait times stochastically to avoid
|
|
136
|
+
A function of the value yielded by `wait_gen_factory()` returning the actual
|
|
137
|
+
time to wait. This function helps distribute wait times stochastically to avoid
|
|
138
138
|
timing collisions across concurrent clients. Wait times are jittered by
|
|
139
139
|
default using the `full_jitter` function. To disable jittering, pass
|
|
140
140
|
`jitter=None`.
|
|
@@ -142,6 +142,13 @@ class RetryInvoker:
|
|
|
142
142
|
A function accepting an exception instance, returning whether or not
|
|
143
143
|
to give up prematurely before other give-up conditions are evaluated.
|
|
144
144
|
If set to None, the strategy is to never give up prematurely.
|
|
145
|
+
wait_function: Optional[Callable[[float], None]] (default: None)
|
|
146
|
+
A function that defines how to wait between retry attempts. It accepts
|
|
147
|
+
one argument, the wait time in seconds, allowing the use of various waiting
|
|
148
|
+
mechanisms (e.g., asynchronous waits or event-based synchronization) suitable
|
|
149
|
+
for different execution environments. If set to `None`, the `wait_function`
|
|
150
|
+
defaults to `time.sleep`, which is ideal for synchronous operations. Custom
|
|
151
|
+
functions should manage execution flow to prevent blocking or interference.
|
|
145
152
|
|
|
146
153
|
Examples
|
|
147
154
|
--------
|
|
@@ -159,7 +166,7 @@ class RetryInvoker:
|
|
|
159
166
|
# pylint: disable-next=too-many-arguments
|
|
160
167
|
def __init__(
|
|
161
168
|
self,
|
|
162
|
-
|
|
169
|
+
wait_gen_factory: Callable[[], Generator[float, None, None]],
|
|
163
170
|
recoverable_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]],
|
|
164
171
|
max_tries: Optional[int],
|
|
165
172
|
max_time: Optional[float],
|
|
@@ -169,8 +176,9 @@ class RetryInvoker:
|
|
|
169
176
|
on_giveup: Optional[Callable[[RetryState], None]] = None,
|
|
170
177
|
jitter: Optional[Callable[[float], float]] = full_jitter,
|
|
171
178
|
should_giveup: Optional[Callable[[Exception], bool]] = None,
|
|
179
|
+
wait_function: Optional[Callable[[float], None]] = None,
|
|
172
180
|
) -> None:
|
|
173
|
-
self.
|
|
181
|
+
self.wait_gen_factory = wait_gen_factory
|
|
174
182
|
self.recoverable_exceptions = recoverable_exceptions
|
|
175
183
|
self.max_tries = max_tries
|
|
176
184
|
self.max_time = max_time
|
|
@@ -179,6 +187,9 @@ class RetryInvoker:
|
|
|
179
187
|
self.on_giveup = on_giveup
|
|
180
188
|
self.jitter = jitter
|
|
181
189
|
self.should_giveup = should_giveup
|
|
190
|
+
if wait_function is None:
|
|
191
|
+
wait_function = time.sleep
|
|
192
|
+
self.wait_function = wait_function
|
|
182
193
|
|
|
183
194
|
# pylint: disable-next=too-many-locals
|
|
184
195
|
def invoke(
|
|
@@ -212,13 +223,13 @@ class RetryInvoker:
|
|
|
212
223
|
Raises
|
|
213
224
|
------
|
|
214
225
|
Exception
|
|
215
|
-
If the number of tries exceeds `max_tries`, if the total time
|
|
216
|
-
|
|
226
|
+
If the number of tries exceeds `max_tries`, if the total time exceeds
|
|
227
|
+
`max_time`, if `wait_gen_factory()` generator raises `StopInteration`,
|
|
217
228
|
or if the `should_giveup` returns True for a raised exception.
|
|
218
229
|
|
|
219
230
|
Notes
|
|
220
231
|
-----
|
|
221
|
-
The time between retries is determined by the provided `
|
|
232
|
+
The time between retries is determined by the provided `wait_gen_factory()`
|
|
222
233
|
generator and can optionally be jittered using the `jitter` function.
|
|
223
234
|
The recoverable exceptions that trigger a retry, as well as conditions to
|
|
224
235
|
stop retries, are also determined by the class's initialization parameters.
|
|
@@ -231,13 +242,13 @@ class RetryInvoker:
|
|
|
231
242
|
handler(cast(RetryState, ref_state[0]))
|
|
232
243
|
|
|
233
244
|
try_cnt = 0
|
|
234
|
-
wait_generator = self.
|
|
235
|
-
start = time.
|
|
245
|
+
wait_generator = self.wait_gen_factory()
|
|
246
|
+
start = time.monotonic()
|
|
236
247
|
ref_state: List[Optional[RetryState]] = [None]
|
|
237
248
|
|
|
238
249
|
while True:
|
|
239
250
|
try_cnt += 1
|
|
240
|
-
elapsed_time = time.
|
|
251
|
+
elapsed_time = time.monotonic() - start
|
|
241
252
|
state = RetryState(
|
|
242
253
|
target=target,
|
|
243
254
|
args=args,
|
|
@@ -282,7 +293,7 @@ class RetryInvoker:
|
|
|
282
293
|
try_call_event_handler(self.on_backoff)
|
|
283
294
|
|
|
284
295
|
# Sleep
|
|
285
|
-
|
|
296
|
+
self.wait_function(state.actual_wait)
|
|
286
297
|
else:
|
|
287
298
|
# Trigger success event
|
|
288
299
|
try_call_event_handler(self.on_success)
|
flwr/server/server_app.py
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
from typing import Callable, Optional
|
|
19
19
|
|
|
20
20
|
from flwr.common import Context, RecordSet
|
|
21
|
+
from flwr.common.logger import warn_preview_feature
|
|
21
22
|
from flwr.server.strategy import Strategy
|
|
22
23
|
|
|
23
24
|
from .client_manager import ClientManager
|
|
@@ -120,6 +121,8 @@ class ServerApp:
|
|
|
120
121
|
""",
|
|
121
122
|
)
|
|
122
123
|
|
|
124
|
+
warn_preview_feature("ServerApp-register-main-function")
|
|
125
|
+
|
|
123
126
|
# Register provided function with the ServerApp object
|
|
124
127
|
self._main = main_fn
|
|
125
128
|
|
|
@@ -63,7 +63,8 @@ def ping(
|
|
|
63
63
|
state: State, # pylint: disable=unused-argument
|
|
64
64
|
) -> PingResponse:
|
|
65
65
|
"""."""
|
|
66
|
-
|
|
66
|
+
res = state.acknowledge_ping(request.node.node_id, request.ping_interval)
|
|
67
|
+
return PingResponse(success=res)
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
def pull_task_ins(request: PullTaskInsRequest, state: State) -> PullTaskInsResponse:
|
|
@@ -21,6 +21,7 @@ from flwr.common.constant import MISSING_EXTRA_REST
|
|
|
21
21
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
22
22
|
CreateNodeRequest,
|
|
23
23
|
DeleteNodeRequest,
|
|
24
|
+
PingRequest,
|
|
24
25
|
PullTaskInsRequest,
|
|
25
26
|
PushTaskResRequest,
|
|
26
27
|
)
|
|
@@ -152,11 +153,38 @@ async def push_task_res(request: Request) -> Response: # Check if token is need
|
|
|
152
153
|
)
|
|
153
154
|
|
|
154
155
|
|
|
156
|
+
async def ping(request: Request) -> Response:
|
|
157
|
+
"""Ping."""
|
|
158
|
+
_check_headers(request.headers)
|
|
159
|
+
|
|
160
|
+
# Get the request body as raw bytes
|
|
161
|
+
ping_request_bytes: bytes = await request.body()
|
|
162
|
+
|
|
163
|
+
# Deserialize ProtoBuf
|
|
164
|
+
ping_request_proto = PingRequest()
|
|
165
|
+
ping_request_proto.ParseFromString(ping_request_bytes)
|
|
166
|
+
|
|
167
|
+
# Get state from app
|
|
168
|
+
state: State = app.state.STATE_FACTORY.state()
|
|
169
|
+
|
|
170
|
+
# Handle message
|
|
171
|
+
ping_response_proto = message_handler.ping(request=ping_request_proto, state=state)
|
|
172
|
+
|
|
173
|
+
# Return serialized ProtoBuf
|
|
174
|
+
ping_response_bytes = ping_response_proto.SerializeToString()
|
|
175
|
+
return Response(
|
|
176
|
+
status_code=200,
|
|
177
|
+
content=ping_response_bytes,
|
|
178
|
+
headers={"Content-Type": "application/protobuf"},
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
155
182
|
routes = [
|
|
156
183
|
Route("/api/v0/fleet/create-node", create_node, methods=["POST"]),
|
|
157
184
|
Route("/api/v0/fleet/delete-node", delete_node, methods=["POST"]),
|
|
158
185
|
Route("/api/v0/fleet/pull-task-ins", pull_task_ins, methods=["POST"]),
|
|
159
186
|
Route("/api/v0/fleet/push-task-res", push_task_res, methods=["POST"]),
|
|
187
|
+
Route("/api/v0/fleet/ping", ping, methods=["POST"]),
|
|
160
188
|
]
|
|
161
189
|
|
|
162
190
|
app: Starlette = Starlette(
|
|
@@ -223,6 +223,7 @@ async def run(
|
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
# pylint: disable=too-many-arguments,unused-argument,too-many-locals,too-many-branches
|
|
226
|
+
# pylint: disable=too-many-statements
|
|
226
227
|
def start_vce(
|
|
227
228
|
backend_name: str,
|
|
228
229
|
backend_config_json_stream: str,
|
|
@@ -341,6 +342,13 @@ def start_vce(
|
|
|
341
342
|
)
|
|
342
343
|
)
|
|
343
344
|
except LoadClientAppError as loadapp_ex:
|
|
345
|
+
f_stop_delay = 10
|
|
346
|
+
log(
|
|
347
|
+
ERROR,
|
|
348
|
+
"LoadClientAppError exception encountered. Terminating simulation in %is",
|
|
349
|
+
f_stop_delay,
|
|
350
|
+
)
|
|
351
|
+
time.sleep(f_stop_delay)
|
|
344
352
|
f_stop.set() # set termination event
|
|
345
353
|
raise loadapp_ex
|
|
346
354
|
except Exception as ex:
|
{flwr_nightly-1.8.0.dev20240328.dist-info → flwr_nightly-1.8.0.dev20240401.dist-info}/RECORD
RENAMED
|
@@ -28,14 +28,15 @@ flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
|
|
|
28
28
|
flwr/cli/run/run.py,sha256=C7Yh-Y0f64PEabb9733jBKIhhOUFpcRmCZJIDtv-NG8,2329
|
|
29
29
|
flwr/cli/utils.py,sha256=_V2BlFVNNG2naZrq227fZ8o4TxBN_hB-4fQsen9uQoo,2300
|
|
30
30
|
flwr/client/__init__.py,sha256=futk_IdY_N1h8BTve4Iru51bxm7H1gv58ZPIXWi5XUA,1187
|
|
31
|
-
flwr/client/app.py,sha256=
|
|
31
|
+
flwr/client/app.py,sha256=3k1qi_OnN17F7Lwz4zmU6O9Ijsvy9UtfCBjy0qZ3EoY,25445
|
|
32
32
|
flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
|
|
33
|
-
flwr/client/client_app.py,sha256=
|
|
33
|
+
flwr/client/client_app.py,sha256=jqZGliZH7unwJ6y1lQOpGjrMvuOt0P5plVveaPeJBXw,8315
|
|
34
34
|
flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
|
|
35
35
|
flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
|
|
36
36
|
flwr/client/grpc_client/connection.py,sha256=w3Lble9-eCzNOR7fBUsVedVCK4ui9QPhK7i7Ew_a5Vk,8717
|
|
37
37
|
flwr/client/grpc_rere_client/__init__.py,sha256=avn6W_vHEM_yZEB1S7hCZgnTbXb6ZujqRP_vAzyXu-0,752
|
|
38
|
-
flwr/client/grpc_rere_client/connection.py,sha256=
|
|
38
|
+
flwr/client/grpc_rere_client/connection.py,sha256=boYRkyesnpdUWQL-DSA660tINCDGxRIPdXT8hc9uHfo,8525
|
|
39
|
+
flwr/client/heartbeat.py,sha256=6Ix2Du9SDlXU_nre48WIDUXDy3AVoZsGKacSq2NqT5c,2377
|
|
39
40
|
flwr/client/message_handler/__init__.py,sha256=abHvBRJJiiaAMNgeILQbMOa6h8WqMK2BcnvxwQZFpic,719
|
|
40
41
|
flwr/client/message_handler/message_handler.py,sha256=ml_FlduAJ5pxO31n1tKRrWfQRSxkMgKLbwXXcRsNSos,6553
|
|
41
42
|
flwr/client/message_handler/task_handler.py,sha256=ZDJBKmrn2grRMNl1rU1iGs7FiMHL5VmZiSp_6h9GHVU,1824
|
|
@@ -51,11 +52,11 @@ flwr/client/node_state.py,sha256=KTTs_l4I0jBM7IsSsbAGjhfL_yZC3QANbzyvyfZBRDM,177
|
|
|
51
52
|
flwr/client/node_state_tests.py,sha256=gPwz0zf2iuDSa11jedkur_u3Xm7lokIDG5ALD2MCvSw,2195
|
|
52
53
|
flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,10283
|
|
53
54
|
flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
|
|
54
|
-
flwr/client/rest_client/connection.py,sha256=
|
|
55
|
+
flwr/client/rest_client/connection.py,sha256=FdK0hQUFBX4jY68wnlRlFJEKADD81fFT8ubhBI0Mm78,14447
|
|
55
56
|
flwr/client/typing.py,sha256=c9EvjlEjasxn1Wqx6bGl6Xg6vM1gMFfmXht-E2i5J-k,1006
|
|
56
57
|
flwr/common/__init__.py,sha256=dHOptgKxna78CEQLD5Yu0QIsoSgpIIw5AhIUZCHDWAU,3721
|
|
57
58
|
flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
|
|
58
|
-
flwr/common/constant.py,sha256=
|
|
59
|
+
flwr/common/constant.py,sha256=brSqgGzA21eDqL8WxYOb-MTYcnsAkT4g0wseyRPE7rI,2084
|
|
59
60
|
flwr/common/context.py,sha256=ounF-mWPPtXGwtae3sg5EhF58ScviOa3MVqxRpGVu-8,1313
|
|
60
61
|
flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
|
|
61
62
|
flwr/common/differential_privacy.py,sha256=WZWrL7C9XaB9l9NDkLDI5PvM7jwcoTTFu08ZVG8-M5Q,6113
|
|
@@ -63,7 +64,7 @@ flwr/common/differential_privacy_constants.py,sha256=c7b7tqgvT7yMK0XN9ndiTBs4mQf
|
|
|
63
64
|
flwr/common/dp.py,sha256=Hc3lLHihjexbJaD_ft31gdv9XRcwOTgDBwJzICuok3A,2004
|
|
64
65
|
flwr/common/exit_handlers.py,sha256=2Nt0wLhc17KQQsLPFSRAjjhUiEFfJK6tNozdGiIY4Fs,2812
|
|
65
66
|
flwr/common/grpc.py,sha256=HimjpTtIY3Vfqtlq3u-CYWjqAl9rSn0uo3A8JjhUmwQ,2273
|
|
66
|
-
flwr/common/logger.py,sha256=
|
|
67
|
+
flwr/common/logger.py,sha256=3hfKun9YISWj4i_QhxgZdnaHJc4x-QvFJQJTKHZ2KHs,6096
|
|
67
68
|
flwr/common/message.py,sha256=vgFSCOkPbl60iS4-XQJ8-rHL54MvNc2AwNSSxVl6qYY,11773
|
|
68
69
|
flwr/common/object_ref.py,sha256=ELoUCAFO-vbjJC41CGpa-WBG2SLYe3ErW-d9YCG3zqA,4961
|
|
69
70
|
flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
|
|
@@ -76,7 +77,7 @@ flwr/common/record/parametersrecord.py,sha256=WSqtRrYvI-mRzkEwv5s-EG-yE5uizJ8zy9
|
|
|
76
77
|
flwr/common/record/recordset.py,sha256=o5UwLubotz1KE9HCoEIP5kK0f0dlIzpFpS1xeQvxo08,3016
|
|
77
78
|
flwr/common/record/typeddict.py,sha256=2NW8JF27p1uNWaqDbJ7bMkItA5x4ygYT8aHrf8NaqnE,3879
|
|
78
79
|
flwr/common/recordset_compat.py,sha256=BjxeuvlCaP94yIiKOyFFTRBUH_lprFWSLo8U8q3BDbs,13798
|
|
79
|
-
flwr/common/retry_invoker.py,sha256=
|
|
80
|
+
flwr/common/retry_invoker.py,sha256=6zpjE5TXw-AuPz6Q4geVW8IU6S8sGaGaehLP3HTmlMw,11669
|
|
80
81
|
flwr/common/secure_aggregation/__init__.py,sha256=29nHIUO2L8-KhNHQ2KmIgRo_4CPkq4LgLCUN0on5FgI,731
|
|
81
82
|
flwr/common/secure_aggregation/crypto/__init__.py,sha256=dz7pVx2aPrHxr_AwgO5mIiTzu4PcvUxRq9NLBbFcsf8,738
|
|
82
83
|
flwr/common/secure_aggregation/crypto/shamir.py,sha256=yY35ZgHlB4YyGW_buG-1X-0M-ejXuQzISgYLgC_Z9TY,2792
|
|
@@ -135,7 +136,7 @@ flwr/server/driver/grpc_driver.py,sha256=D2n3_Es_DHFgQsq_TjYVEz8RYJJJYoe24E1voza
|
|
|
135
136
|
flwr/server/history.py,sha256=hDsoBaA4kUa6d1yvDVXuLluBqOBKSm0_fVDtUtYJkmg,5121
|
|
136
137
|
flwr/server/run_serverapp.py,sha256=3hoXa57T4L1vOWVWPSSdZ_UyRO-uTwUIrhha6TJAXMg,5592
|
|
137
138
|
flwr/server/server.py,sha256=UnBRlI6AGTj0nKeRtEQ3IalM3TJmggMKXhDyn8yKZNk,17664
|
|
138
|
-
flwr/server/server_app.py,sha256=
|
|
139
|
+
flwr/server/server_app.py,sha256=KgAT_HqsfseTLNnfX2ph42PBbVqQ0lFzvYrT90V34y0,4402
|
|
139
140
|
flwr/server/server_config.py,sha256=CZaHVAsMvGLjpWVcLPkiYxgJN4xfIyAiUrCI3fETKY4,1349
|
|
140
141
|
flwr/server/strategy/__init__.py,sha256=7eVZ3hQEg2BgA_usAeL6tsLp9T6XI1VYYoFy08Xn-ew,2836
|
|
141
142
|
flwr/server/strategy/aggregate.py,sha256=QyRIJtI5gnuY1NbgrcrOvkHxGIxBvApq7d9Y4xl-6W4,13468
|
|
@@ -174,14 +175,14 @@ flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=1QyBX5qcFPjMVlv7Trvn
|
|
|
174
175
|
flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=bEJOMWbSlqkw-y5ZHtEXczhoSlAxErcRYffmTMQAV8M,758
|
|
175
176
|
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=LC_ntiLZMIZkspwjtQ9_MZ4agzArebO4HIVJ3YOrFx8,3036
|
|
176
177
|
flwr/server/superlink/fleet/message_handler/__init__.py,sha256=hEY0l61ojH8Iz30_K1btm1HJ6J49iZJSFUsVYqUTw3A,731
|
|
177
|
-
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=
|
|
178
|
+
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=jz8l9xZX_ekv_TUv7pdYsByUWbvnLj1PfsOdct1gZ38,3238
|
|
178
179
|
flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=VKDvDq5H8koOUztpmQacVzGJXPLEEkL1Vmolxt3mvnY,735
|
|
179
|
-
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=
|
|
180
|
+
flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=_tGtARm4x957Fu1EWoDieqOzV9CQZTM4GgKe2GxIOvw,6734
|
|
180
181
|
flwr/server/superlink/fleet/vce/__init__.py,sha256=36MHKiefnJeyjwMQzVUK4m06Ojon3WDcwZGQsAcyVhQ,783
|
|
181
182
|
flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=oBIzmnrSSRvH_H0vRGEGWhWzQQwqe3zn6e13RsNwlIY,1466
|
|
182
183
|
flwr/server/superlink/fleet/vce/backend/backend.py,sha256=LJsKl7oixVvptcG98Rd9ejJycNWcEVB0ODvSreLGp-A,2260
|
|
183
184
|
flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=TaT2EpbVEsIY0EDzF8obadyZaSXjD38TFGdDPI-ytD0,6375
|
|
184
|
-
flwr/server/superlink/fleet/vce/vce_api.py,sha256=
|
|
185
|
+
flwr/server/superlink/fleet/vce/vce_api.py,sha256=8zfJf2_BtvxZHI0-Konpa6sTg9l4KXOLgCIkt38C6CE,12041
|
|
185
186
|
flwr/server/superlink/state/__init__.py,sha256=ij-7Ms-hyordQdRmGQxY1-nVa4OhixJ0jr7_YDkys0s,1003
|
|
186
187
|
flwr/server/superlink/state/in_memory_state.py,sha256=XfdCGRzFut9xlf7AsDAhhAmBw-nKDBjmPWAI--espj0,8707
|
|
187
188
|
flwr/server/superlink/state/sqlite_state.py,sha256=1SR6Zz6ud0tSSx940gTfa0vct_GH2n0cX_vnhoAEMlQ,22005
|
|
@@ -204,8 +205,8 @@ flwr/simulation/ray_transport/ray_actor.py,sha256=OWjgYW--fswkEDqTP9L_cZblBeUVL5
|
|
|
204
205
|
flwr/simulation/ray_transport/ray_client_proxy.py,sha256=oDu4sEPIOu39vrNi-fqDAe10xtNUXMO49bM2RWfRcyw,6738
|
|
205
206
|
flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
|
|
206
207
|
flwr/simulation/run_simulation.py,sha256=HiIH6aa_v56NfKQN5ZBd94NyVfaZNyFs43_kItYsQXU,15685
|
|
207
|
-
flwr_nightly-1.8.0.
|
|
208
|
-
flwr_nightly-1.8.0.
|
|
209
|
-
flwr_nightly-1.8.0.
|
|
210
|
-
flwr_nightly-1.8.0.
|
|
211
|
-
flwr_nightly-1.8.0.
|
|
208
|
+
flwr_nightly-1.8.0.dev20240401.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
209
|
+
flwr_nightly-1.8.0.dev20240401.dist-info/METADATA,sha256=9EApt2x0ajI0-A4ms8brLZ45cWsrgTPSmrKs6X1maPs,15257
|
|
210
|
+
flwr_nightly-1.8.0.dev20240401.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
211
|
+
flwr_nightly-1.8.0.dev20240401.dist-info/entry_points.txt,sha256=utu2wybGyYJSTtsB2ktY_gmy-XtMFo9EFZdishX0zR4,320
|
|
212
|
+
flwr_nightly-1.8.0.dev20240401.dist-info/RECORD,,
|
{flwr_nightly-1.8.0.dev20240328.dist-info → flwr_nightly-1.8.0.dev20240401.dist-info}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|