flwr-nightly 1.20.0.dev20250720__py3-none-any.whl → 1.20.0.dev20250721__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/grpc_adapter_client/connection.py +5 -4
- flwr/client/grpc_rere_client/connection.py +34 -92
- flwr/client/rest_client/connection.py +39 -120
- flwr/supernode/runtime/run_clientapp.py +2 -1
- flwr/supernode/start_client_internal.py +62 -23
- {flwr_nightly-1.20.0.dev20250720.dist-info → flwr_nightly-1.20.0.dev20250721.dist-info}/METADATA +1 -1
- {flwr_nightly-1.20.0.dev20250720.dist-info → flwr_nightly-1.20.0.dev20250721.dist-info}/RECORD +9 -9
- {flwr_nightly-1.20.0.dev20250720.dist-info → flwr_nightly-1.20.0.dev20250721.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.20.0.dev20250720.dist-info → flwr_nightly-1.20.0.dev20250721.dist-info}/entry_points.txt +0 -0
@@ -29,6 +29,7 @@ from flwr.common.logger import log
|
|
29
29
|
from flwr.common.message import Message
|
30
30
|
from flwr.common.retry_invoker import RetryInvoker
|
31
31
|
from flwr.common.typing import Fab, Run
|
32
|
+
from flwr.proto.message_pb2 import ObjectTree # pylint: disable=E0611
|
32
33
|
|
33
34
|
|
34
35
|
@contextmanager
|
@@ -43,8 +44,8 @@ def grpc_adapter( # pylint: disable=R0913,too-many-positional-arguments
|
|
43
44
|
] = None,
|
44
45
|
) -> Iterator[
|
45
46
|
tuple[
|
46
|
-
Callable[[], Optional[Message]],
|
47
|
-
Callable[[Message],
|
47
|
+
Callable[[], Optional[tuple[Message, ObjectTree]]],
|
48
|
+
Callable[[Message, ObjectTree], set[str]],
|
48
49
|
Callable[[], Optional[int]],
|
49
50
|
Callable[[], None],
|
50
51
|
Callable[[int], Run],
|
@@ -80,8 +81,8 @@ def grpc_adapter( # pylint: disable=R0913,too-many-positional-arguments
|
|
80
81
|
|
81
82
|
Returns
|
82
83
|
-------
|
83
|
-
receive : Callable
|
84
|
-
send : Callable
|
84
|
+
receive : Callable[[], Optional[tuple[Message, ObjectTree]]]
|
85
|
+
send : Callable[[Message, ObjectTree], set[str]]
|
85
86
|
create_node : Optional[Callable]
|
86
87
|
delete_node : Optional[Callable]
|
87
88
|
get_run : Optional[Callable]
|
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
from collections.abc import Iterator, Sequence
|
19
19
|
from contextlib import contextmanager
|
20
|
-
from logging import
|
20
|
+
from logging import ERROR
|
21
21
|
from pathlib import Path
|
22
22
|
from typing import Callable, Optional, Union, cast
|
23
23
|
|
@@ -28,29 +28,18 @@ from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
28
28
|
from flwr.common.constant import HEARTBEAT_CALL_TIMEOUT, HEARTBEAT_DEFAULT_INTERVAL
|
29
29
|
from flwr.common.grpc import create_channel, on_channel_state_change
|
30
30
|
from flwr.common.heartbeat import HeartbeatSender
|
31
|
-
from flwr.common.inflatable import (
|
32
|
-
get_all_nested_objects,
|
33
|
-
get_object_tree,
|
34
|
-
iterate_object_tree,
|
35
|
-
no_object_id_recompute,
|
36
|
-
)
|
37
31
|
from flwr.common.inflatable_protobuf_utils import (
|
38
32
|
make_confirm_message_received_fn_protobuf,
|
39
33
|
make_pull_object_fn_protobuf,
|
40
34
|
make_push_object_fn_protobuf,
|
41
35
|
)
|
42
|
-
from flwr.common.inflatable_utils import (
|
43
|
-
inflate_object_from_contents,
|
44
|
-
pull_objects,
|
45
|
-
push_objects,
|
46
|
-
)
|
47
36
|
from flwr.common.logger import log
|
48
37
|
from flwr.common.message import Message, remove_content_from_message
|
49
38
|
from flwr.common.retry_invoker import RetryInvoker, _wrap_stub
|
50
39
|
from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
|
51
40
|
generate_key_pairs,
|
52
41
|
)
|
53
|
-
from flwr.common.serde import message_to_proto, run_from_proto
|
42
|
+
from flwr.common.serde import message_from_proto, message_to_proto, run_from_proto
|
54
43
|
from flwr.common.typing import Fab, Run, RunNotRunningException
|
55
44
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
56
45
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
@@ -66,9 +55,7 @@ from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
|
|
66
55
|
SendNodeHeartbeatRequest,
|
67
56
|
SendNodeHeartbeatResponse,
|
68
57
|
)
|
69
|
-
from flwr.proto.message_pb2 import
|
70
|
-
ConfirmMessageReceivedRequest,
|
71
|
-
)
|
58
|
+
from flwr.proto.message_pb2 import ObjectTree # pylint: disable=E0611
|
72
59
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
73
60
|
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
74
61
|
|
@@ -89,8 +76,8 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
89
76
|
adapter_cls: Optional[Union[type[FleetStub], type[GrpcAdapter]]] = None,
|
90
77
|
) -> Iterator[
|
91
78
|
tuple[
|
92
|
-
Callable[[], Optional[Message]],
|
93
|
-
Callable[[Message],
|
79
|
+
Callable[[], Optional[tuple[Message, ObjectTree]]],
|
80
|
+
Callable[[Message, ObjectTree], set[str]],
|
94
81
|
Callable[[], Optional[int]],
|
95
82
|
Callable[[], None],
|
96
83
|
Callable[[int], Run],
|
@@ -253,98 +240,53 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
253
240
|
# Cleanup
|
254
241
|
node = None
|
255
242
|
|
256
|
-
def receive() -> Optional[Message]:
|
257
|
-
"""
|
243
|
+
def receive() -> Optional[tuple[Message, ObjectTree]]:
|
244
|
+
"""Pull a message with its ObjectTree from SuperLink."""
|
258
245
|
# Get Node
|
259
246
|
if node is None:
|
260
247
|
log(ERROR, "Node instance missing")
|
261
248
|
return None
|
262
249
|
|
263
|
-
#
|
250
|
+
# Try to pull a message with its object tree from SuperLink
|
264
251
|
request = PullMessagesRequest(node=node)
|
265
252
|
response: PullMessagesResponse = stub.PullMessages(request=request)
|
266
253
|
|
267
|
-
#
|
268
|
-
|
269
|
-
None
|
270
|
-
)
|
254
|
+
# If no messages are available, return None
|
255
|
+
if len(response.messages_list) == 0:
|
256
|
+
return None
|
271
257
|
|
272
|
-
#
|
273
|
-
|
274
|
-
|
275
|
-
):
|
276
|
-
message_proto = None
|
258
|
+
# Get the current Message and its object tree
|
259
|
+
message_proto = response.messages_list[0]
|
260
|
+
object_tree = response.message_object_trees[0]
|
277
261
|
|
278
262
|
# Construct the Message
|
279
|
-
in_message
|
280
|
-
|
281
|
-
if message_proto:
|
282
|
-
msg_id = message_proto.metadata.message_id
|
283
|
-
run_id = message_proto.metadata.run_id
|
284
|
-
object_tree = response.message_object_trees[0]
|
285
|
-
all_object_contents = pull_objects(
|
286
|
-
[tree.object_id for tree in iterate_object_tree(object_tree)],
|
287
|
-
pull_object_fn=make_pull_object_fn_protobuf(
|
288
|
-
pull_object_protobuf=stub.PullObject,
|
289
|
-
node=node,
|
290
|
-
run_id=run_id,
|
291
|
-
),
|
292
|
-
)
|
293
|
-
|
294
|
-
# Confirm that the message has been received
|
295
|
-
stub.ConfirmMessageReceived(
|
296
|
-
ConfirmMessageReceivedRequest(
|
297
|
-
node=node, run_id=run_id, message_object_id=msg_id
|
298
|
-
)
|
299
|
-
)
|
263
|
+
in_message = message_from_proto(message_proto)
|
300
264
|
|
301
|
-
|
302
|
-
|
303
|
-
)
|
304
|
-
# The deflated message doesn't contain the message_id (its own object_id)
|
305
|
-
# Inject
|
306
|
-
in_message.metadata.__dict__["_message_id"] = msg_id
|
307
|
-
|
308
|
-
# Return the message if available
|
309
|
-
return in_message
|
265
|
+
# Return the Message and its object tree
|
266
|
+
return in_message, object_tree
|
310
267
|
|
311
|
-
def send(message: Message) ->
|
312
|
-
"""Send message
|
268
|
+
def send(message: Message, object_tree: ObjectTree) -> set[str]:
|
269
|
+
"""Send the message with its ObjectTree to SuperLink."""
|
313
270
|
# Get Node
|
314
271
|
if node is None:
|
315
272
|
log(ERROR, "Node instance missing")
|
316
|
-
return
|
273
|
+
return set()
|
317
274
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
object_tree = get_object_tree(message)
|
275
|
+
# Remove the content from the message if it has
|
276
|
+
if message.has_content():
|
277
|
+
message = remove_content_from_message(message)
|
322
278
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
)
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
if response.objects_to_push:
|
335
|
-
objs_to_push = set(
|
336
|
-
response.objects_to_push[message.object_id].object_ids
|
337
|
-
)
|
338
|
-
push_objects(
|
339
|
-
all_objects,
|
340
|
-
push_object_fn=make_push_object_fn_protobuf(
|
341
|
-
push_object_protobuf=stub.PushObject,
|
342
|
-
node=node,
|
343
|
-
run_id=message.metadata.run_id,
|
344
|
-
),
|
345
|
-
object_ids_to_push=objs_to_push,
|
346
|
-
)
|
347
|
-
log(DEBUG, "Pushed %s objects to servicer.", len(objs_to_push))
|
279
|
+
# Send the message with its ObjectTree to SuperLink
|
280
|
+
request = PushMessagesRequest(
|
281
|
+
node=node,
|
282
|
+
messages_list=[message_to_proto(message)],
|
283
|
+
message_object_trees=[object_tree],
|
284
|
+
)
|
285
|
+
response: PushMessagesResponse = stub.PushMessages(request=request)
|
286
|
+
|
287
|
+
# Get and return the object IDs to push
|
288
|
+
object_ids_to_push = response.objects_to_push[object_tree.object_id]
|
289
|
+
return set(object_ids_to_push.object_ids)
|
348
290
|
|
349
291
|
def get_run(run_id: int) -> Run:
|
350
292
|
# Call FleetAPI
|
@@ -17,8 +17,8 @@
|
|
17
17
|
|
18
18
|
from collections.abc import Iterator
|
19
19
|
from contextlib import contextmanager
|
20
|
-
from logging import
|
21
|
-
from typing import Callable, Optional, TypeVar, Union
|
20
|
+
from logging import ERROR, WARN
|
21
|
+
from typing import Callable, Optional, TypeVar, Union
|
22
22
|
|
23
23
|
from cryptography.hazmat.primitives.asymmetric import ec
|
24
24
|
from google.protobuf.message import Message as GrpcMessage
|
@@ -28,26 +28,15 @@ from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
28
28
|
from flwr.common.constant import HEARTBEAT_DEFAULT_INTERVAL
|
29
29
|
from flwr.common.exit import ExitCode, flwr_exit
|
30
30
|
from flwr.common.heartbeat import HeartbeatSender
|
31
|
-
from flwr.common.inflatable import (
|
32
|
-
get_all_nested_objects,
|
33
|
-
get_object_tree,
|
34
|
-
iterate_object_tree,
|
35
|
-
no_object_id_recompute,
|
36
|
-
)
|
37
31
|
from flwr.common.inflatable_protobuf_utils import (
|
38
32
|
make_confirm_message_received_fn_protobuf,
|
39
33
|
make_pull_object_fn_protobuf,
|
40
34
|
make_push_object_fn_protobuf,
|
41
35
|
)
|
42
|
-
from flwr.common.inflatable_utils import (
|
43
|
-
inflate_object_from_contents,
|
44
|
-
pull_objects,
|
45
|
-
push_objects,
|
46
|
-
)
|
47
36
|
from flwr.common.logger import log
|
48
37
|
from flwr.common.message import Message, remove_content_from_message
|
49
38
|
from flwr.common.retry_invoker import RetryInvoker
|
50
|
-
from flwr.common.serde import message_to_proto, run_from_proto
|
39
|
+
from flwr.common.serde import message_from_proto, message_to_proto, run_from_proto
|
51
40
|
from flwr.common.typing import Fab, Run
|
52
41
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
53
42
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
@@ -67,6 +56,7 @@ from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
|
|
67
56
|
from flwr.proto.message_pb2 import ( # pylint: disable=E0611
|
68
57
|
ConfirmMessageReceivedRequest,
|
69
58
|
ConfirmMessageReceivedResponse,
|
59
|
+
ObjectTree,
|
70
60
|
PullObjectRequest,
|
71
61
|
PullObjectResponse,
|
72
62
|
PushObjectRequest,
|
@@ -109,8 +99,8 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
109
99
|
] = None,
|
110
100
|
) -> Iterator[
|
111
101
|
tuple[
|
112
|
-
Callable[[], Optional[Message]],
|
113
|
-
Callable[[Message],
|
102
|
+
Callable[[], Optional[tuple[Message, ObjectTree]]],
|
103
|
+
Callable[[Message, ObjectTree], set[str]],
|
114
104
|
Callable[[], Optional[int]],
|
115
105
|
Callable[[], None],
|
116
106
|
Callable[[int], Run],
|
@@ -316,8 +306,7 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
316
306
|
"""Set delete_node."""
|
317
307
|
nonlocal node
|
318
308
|
if node is None:
|
319
|
-
|
320
|
-
return
|
309
|
+
raise RuntimeError("Node instance missing")
|
321
310
|
|
322
311
|
# Stop the heartbeat sender
|
323
312
|
heartbeat_sender.stop()
|
@@ -333,125 +322,55 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
333
322
|
# Cleanup
|
334
323
|
node = None
|
335
324
|
|
336
|
-
def receive() -> Optional[Message]:
|
337
|
-
"""
|
325
|
+
def receive() -> Optional[tuple[Message, ObjectTree]]:
|
326
|
+
"""Pull a message with its ObjectTree from SuperLink."""
|
338
327
|
# Get Node
|
339
328
|
if node is None:
|
340
|
-
|
341
|
-
return None
|
329
|
+
raise RuntimeError("Node instance missing")
|
342
330
|
|
343
|
-
#
|
331
|
+
# Try to pull a message with its object tree from SuperLink
|
344
332
|
req = PullMessagesRequest(node=node)
|
345
|
-
|
346
|
-
# Send the request
|
347
333
|
res = _request(req, PullMessagesResponse, PATH_PULL_MESSAGES)
|
348
334
|
if res is None:
|
349
|
-
|
335
|
+
raise ValueError("PushMessagesResponse is None.")
|
350
336
|
|
351
|
-
#
|
352
|
-
|
337
|
+
# If no messages are available, return None
|
338
|
+
if len(res.messages_list) == 0:
|
339
|
+
return None
|
353
340
|
|
354
|
-
#
|
355
|
-
|
356
|
-
|
357
|
-
):
|
358
|
-
message_proto = None
|
341
|
+
# Get the current Message and its object tree
|
342
|
+
message_proto = res.messages_list[0]
|
343
|
+
object_tree = res.message_object_trees[0]
|
359
344
|
|
360
345
|
# Construct the Message
|
361
|
-
in_message
|
362
|
-
|
363
|
-
if message_proto:
|
364
|
-
log(INFO, "[Node] POST /%s: success", PATH_PULL_MESSAGES)
|
365
|
-
msg_id = message_proto.metadata.message_id
|
366
|
-
run_id = message_proto.metadata.run_id
|
367
|
-
|
368
|
-
try:
|
369
|
-
object_tree = res.message_object_trees[0]
|
370
|
-
all_object_contents = pull_objects(
|
371
|
-
[tree.object_id for tree in iterate_object_tree(object_tree)],
|
372
|
-
pull_object_fn=make_pull_object_fn_protobuf(
|
373
|
-
pull_object_protobuf=_pull_object_protobuf,
|
374
|
-
node=node,
|
375
|
-
run_id=run_id,
|
376
|
-
),
|
377
|
-
)
|
378
|
-
|
379
|
-
# Confirm that the message has been received
|
380
|
-
_request(
|
381
|
-
req=ConfirmMessageReceivedRequest(
|
382
|
-
node=node, run_id=run_id, message_object_id=msg_id
|
383
|
-
),
|
384
|
-
res_type=ConfirmMessageReceivedResponse,
|
385
|
-
api_path=PATH_CONFIRM_MESSAGE_RECEIVED,
|
386
|
-
)
|
387
|
-
except ValueError as e:
|
388
|
-
log(
|
389
|
-
ERROR,
|
390
|
-
"Pulling objects failed. Potential irrecoverable error: %s",
|
391
|
-
str(e),
|
392
|
-
)
|
393
|
-
in_message = cast(
|
394
|
-
Message, inflate_object_from_contents(msg_id, all_object_contents)
|
395
|
-
)
|
396
|
-
# The deflated message doesn't contain the message_id (its own object_id)
|
397
|
-
# Inject
|
398
|
-
in_message.metadata.__dict__["_message_id"] = msg_id
|
346
|
+
in_message = message_from_proto(message_proto)
|
399
347
|
|
400
|
-
|
348
|
+
# Return the Message and its object tree
|
349
|
+
return in_message, object_tree
|
401
350
|
|
402
|
-
def send(message: Message) ->
|
403
|
-
"""Send
|
351
|
+
def send(message: Message, object_tree: ObjectTree) -> set[str]:
|
352
|
+
"""Send the message with its ObjectTree to SuperLink."""
|
404
353
|
# Get Node
|
405
354
|
if node is None:
|
406
|
-
|
407
|
-
return
|
355
|
+
raise RuntimeError("Node instance missing")
|
408
356
|
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
object_tree = get_object_tree(message)
|
357
|
+
# Remove the content from the message if it has
|
358
|
+
if message.has_content():
|
359
|
+
message = remove_content_from_message(message)
|
413
360
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
)
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
)
|
361
|
+
# Send the message with its ObjectTree to SuperLink
|
362
|
+
req = PushMessagesRequest(
|
363
|
+
node=node,
|
364
|
+
messages_list=[message_to_proto(message)],
|
365
|
+
message_object_trees=[object_tree],
|
366
|
+
)
|
367
|
+
res = _request(req, PushMessagesResponse, PATH_PUSH_MESSAGES)
|
368
|
+
if res is None:
|
369
|
+
raise ValueError("PushMessagesResponse is None.")
|
423
370
|
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
log(
|
428
|
-
INFO,
|
429
|
-
"[Node] POST /%s: success, created result %s",
|
430
|
-
PATH_PUSH_MESSAGES,
|
431
|
-
res.results, # pylint: disable=no-member
|
432
|
-
)
|
433
|
-
|
434
|
-
if res and res.objects_to_push:
|
435
|
-
objs_to_push = set(res.objects_to_push[message.object_id].object_ids)
|
436
|
-
|
437
|
-
try:
|
438
|
-
push_objects(
|
439
|
-
all_objects,
|
440
|
-
push_object_fn=make_push_object_fn_protobuf(
|
441
|
-
push_object_protobuf=_push_object_protobuf,
|
442
|
-
node=node,
|
443
|
-
run_id=message_proto.metadata.run_id,
|
444
|
-
),
|
445
|
-
object_ids_to_push=objs_to_push,
|
446
|
-
)
|
447
|
-
log(DEBUG, "Pushed %s objects to servicer.", len(objs_to_push))
|
448
|
-
except ValueError as e:
|
449
|
-
log(
|
450
|
-
ERROR,
|
451
|
-
"Pushing objects failed. Potential irrecoverable error: %s",
|
452
|
-
str(e),
|
453
|
-
)
|
454
|
-
log(ERROR, str(e))
|
371
|
+
# Get and return the object IDs to push
|
372
|
+
object_ids_to_push = res.objects_to_push[object_tree.object_id]
|
373
|
+
return set(object_ids_to_push.object_ids)
|
455
374
|
|
456
375
|
def get_run(run_id: int) -> Run:
|
457
376
|
# Construct the request
|
@@ -44,6 +44,7 @@ from flwr.common.inflatable_protobuf_utils import (
|
|
44
44
|
)
|
45
45
|
from flwr.common.inflatable_utils import pull_and_inflate_object_from_tree, push_objects
|
46
46
|
from flwr.common.logger import log
|
47
|
+
from flwr.common.message import remove_content_from_message
|
47
48
|
from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
|
48
49
|
from flwr.common.serde import (
|
49
50
|
context_from_proto,
|
@@ -256,7 +257,7 @@ def push_clientappoutputs(
|
|
256
257
|
log(INFO, "[flwr-clientapp] Push `ClientAppOutputs` for token %s", masked_token)
|
257
258
|
# Set message ID
|
258
259
|
message.metadata.__dict__["_message_id"] = message.object_id
|
259
|
-
proto_message = message_to_proto(message)
|
260
|
+
proto_message = message_to_proto(remove_content_from_message(message))
|
260
261
|
proto_context = context_to_proto(context)
|
261
262
|
|
262
263
|
try:
|
@@ -20,6 +20,7 @@ import subprocess
|
|
20
20
|
import time
|
21
21
|
from collections.abc import Iterator
|
22
22
|
from contextlib import contextmanager
|
23
|
+
from functools import partial
|
23
24
|
from logging import INFO, WARN
|
24
25
|
from pathlib import Path
|
25
26
|
from typing import Callable, Optional, Union
|
@@ -47,16 +48,17 @@ from flwr.common.constant import (
|
|
47
48
|
from flwr.common.exit import ExitCode, flwr_exit
|
48
49
|
from flwr.common.exit_handlers import register_exit_handlers
|
49
50
|
from flwr.common.grpc import generic_create_grpc_server
|
50
|
-
from flwr.common.inflatable import
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
from flwr.common.inflatable import iterate_object_tree
|
52
|
+
from flwr.common.inflatable_utils import (
|
53
|
+
pull_objects,
|
54
|
+
push_object_contents_from_iterable,
|
54
55
|
)
|
55
56
|
from flwr.common.logger import log
|
56
57
|
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
57
58
|
from flwr.common.telemetry import EventType
|
58
59
|
from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
|
59
60
|
from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
|
61
|
+
from flwr.proto.message_pb2 import ObjectTree # pylint: disable=E0611
|
60
62
|
from flwr.supercore.ffs import Ffs, FfsFactory
|
61
63
|
from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
|
62
64
|
from flwr.supernode.nodestate import NodeState, NodeStateFactory
|
@@ -237,7 +239,7 @@ def start_client_internal(
|
|
237
239
|
|
238
240
|
_push_messages(
|
239
241
|
state=state,
|
240
|
-
|
242
|
+
object_store=store,
|
241
243
|
send=send,
|
242
244
|
push_object=push_object,
|
243
245
|
)
|
@@ -251,11 +253,11 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
251
253
|
ffs: Ffs,
|
252
254
|
object_store: ObjectStore,
|
253
255
|
node_config: UserConfig,
|
254
|
-
receive: Callable[[], Optional[Message]],
|
256
|
+
receive: Callable[[], Optional[tuple[Message, ObjectTree]]],
|
255
257
|
get_run: Callable[[int], Run],
|
256
258
|
get_fab: Callable[[str, int], Fab],
|
257
|
-
pull_object: Callable[[int, str], bytes],
|
258
|
-
confirm_message_received: Callable[[int, str], None],
|
259
|
+
pull_object: Callable[[int, str], bytes],
|
260
|
+
confirm_message_received: Callable[[int, str], None],
|
259
261
|
) -> Optional[int]:
|
260
262
|
"""Pull a message from the SuperLink and store it in the state.
|
261
263
|
|
@@ -267,8 +269,9 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
267
269
|
message = None
|
268
270
|
try:
|
269
271
|
# Pull message
|
270
|
-
if (
|
272
|
+
if (recv := receive()) is None:
|
271
273
|
return None
|
274
|
+
message, object_tree = recv
|
272
275
|
|
273
276
|
# Log message reception
|
274
277
|
log(INFO, "")
|
@@ -312,16 +315,22 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
312
315
|
)
|
313
316
|
state.store_context(run_ctx)
|
314
317
|
|
315
|
-
#
|
316
|
-
|
318
|
+
# Preregister the object tree of the message
|
319
|
+
obj_ids_to_pull = object_store.preregister(run_id, object_tree)
|
320
|
+
|
321
|
+
# Store the message in the state (note this message has no content)
|
317
322
|
state.store_message(message)
|
318
323
|
|
319
|
-
#
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
324
|
+
# Pull and store objects of the message in the ObjectStore
|
325
|
+
obj_contents = pull_objects(
|
326
|
+
obj_ids_to_pull,
|
327
|
+
pull_object_fn=lambda obj_id: pull_object(run_id, obj_id),
|
328
|
+
)
|
329
|
+
for obj_id in list(obj_contents.keys()):
|
330
|
+
object_store.put(obj_id, obj_contents.pop(obj_id))
|
331
|
+
|
332
|
+
# Confirm that the message was received
|
333
|
+
confirm_message_received(run_id, message.metadata.message_id)
|
325
334
|
|
326
335
|
except RunNotRunningException:
|
327
336
|
if message is None:
|
@@ -344,9 +353,9 @@ def _pull_and_store_message( # pylint: disable=too-many-positional-arguments
|
|
344
353
|
|
345
354
|
def _push_messages(
|
346
355
|
state: NodeState,
|
347
|
-
|
348
|
-
send: Callable[[Message],
|
349
|
-
push_object: Callable[[int, str, bytes], None],
|
356
|
+
object_store: ObjectStore,
|
357
|
+
send: Callable[[Message, ObjectTree], set[str]],
|
358
|
+
push_object: Callable[[int, str, bytes], None],
|
350
359
|
) -> None:
|
351
360
|
"""Push reply messages to the SuperLink."""
|
352
361
|
# Get messages to send
|
@@ -370,9 +379,34 @@ def _push_messages(
|
|
370
379
|
message.metadata.message_type,
|
371
380
|
)
|
372
381
|
|
382
|
+
# Get the object tree for the message
|
383
|
+
object_tree = object_store.get_object_tree(message.metadata.message_id)
|
384
|
+
|
385
|
+
# Define the iterator for yielding object contents
|
386
|
+
# This will yield (object_id, content) pairs
|
387
|
+
def yield_object_contents(_obj_tree: ObjectTree) -> Iterator[tuple[str, bytes]]:
|
388
|
+
for tree in iterate_object_tree(_obj_tree):
|
389
|
+
while (content := object_store.get(tree.object_id)) is None:
|
390
|
+
# Wait for the content to be available
|
391
|
+
time.sleep(0.5)
|
392
|
+
|
393
|
+
yield tree.object_id, content
|
394
|
+
|
373
395
|
# Send the message
|
374
396
|
try:
|
375
|
-
|
397
|
+
# Send the reply message with its ObjectTree
|
398
|
+
send(message, object_tree)
|
399
|
+
|
400
|
+
# Push object contents from the ObjectStore
|
401
|
+
run_id = message.metadata.run_id
|
402
|
+
push_object_contents_from_iterable(
|
403
|
+
yield_object_contents(object_tree),
|
404
|
+
# Use functools.partial to bind run_id explicitly,
|
405
|
+
# avoiding late binding issues and satisfying flake8 (B023)
|
406
|
+
# Equivalent to:
|
407
|
+
# lambda object_id, content: push_object(run_id, object_id, content)
|
408
|
+
push_object_fn=partial(push_object, run_id),
|
409
|
+
)
|
376
410
|
log(INFO, "Sent successfully")
|
377
411
|
except RunNotRunningException:
|
378
412
|
log(
|
@@ -390,6 +424,11 @@ def _push_messages(
|
|
390
424
|
]
|
391
425
|
)
|
392
426
|
|
427
|
+
# Delete all its objects from the ObjectStore
|
428
|
+
# No need to delete objects of the message it replies to, as it is
|
429
|
+
# already deleted when the ClientApp calls `ConfirmMessageReceived`
|
430
|
+
object_store.delete(message.metadata.message_id)
|
431
|
+
|
393
432
|
|
394
433
|
@contextmanager
|
395
434
|
def _init_connection( # pylint: disable=too-many-positional-arguments
|
@@ -404,8 +443,8 @@ def _init_connection( # pylint: disable=too-many-positional-arguments
|
|
404
443
|
max_wait_time: Optional[float] = None,
|
405
444
|
) -> Iterator[
|
406
445
|
tuple[
|
407
|
-
Callable[[], Optional[Message]],
|
408
|
-
Callable[[Message],
|
446
|
+
Callable[[], Optional[tuple[Message, ObjectTree]]],
|
447
|
+
Callable[[Message, ObjectTree], set[str]],
|
409
448
|
Callable[[], Optional[int]],
|
410
449
|
Callable[[], None],
|
411
450
|
Callable[[int], Run],
|
{flwr_nightly-1.20.0.dev20250720.dist-info → flwr_nightly-1.20.0.dev20250721.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.20.0.
|
3
|
+
Version: 1.20.0.dev20250721
|
4
4
|
Summary: Flower: A Friendly Federated AI Framework
|
5
5
|
License: Apache-2.0
|
6
6
|
Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
|
{flwr_nightly-1.20.0.dev20250720.dist-info → flwr_nightly-1.20.0.dev20250721.dist-info}/RECORD
RENAMED
@@ -81,10 +81,10 @@ flwr/client/clientapp/__init__.py,sha256=Zw9qP5nHFnJ9K1dcR4cdY0fRqN-FaMYFSHJFXoF
|
|
81
81
|
flwr/client/clientapp/utils.py,sha256=LsiW1OL2VPcjom3xN29pgBQC0UrttQ-xWL_GF1fkKDo,4344
|
82
82
|
flwr/client/dpfedavg_numpy_client.py,sha256=3hul067cT2E9jBhzp7bFnFAZ_D2nWcIUEdHYE05FpzU,7404
|
83
83
|
flwr/client/grpc_adapter_client/__init__.py,sha256=RQWP5mFPROLHKgombiRvPXVWSoVrQ81wvZm0-lOuuBA,742
|
84
|
-
flwr/client/grpc_adapter_client/connection.py,sha256=
|
84
|
+
flwr/client/grpc_adapter_client/connection.py,sha256=JGv02EjSOAG1E5BRUD4lwXc1LLiYJ0OhInvp31qx1cU,4404
|
85
85
|
flwr/client/grpc_rere_client/__init__.py,sha256=i7iS0Lt8B7q0E2L72e4F_YrKm6ClRKnd71PNA6PW2O0,752
|
86
86
|
flwr/client/grpc_rere_client/client_interceptor.py,sha256=zFaVHw6AxeNO-7eCKKb-RxrPa7zbM5Z-2-1Efc4adQY,2451
|
87
|
-
flwr/client/grpc_rere_client/connection.py,sha256=
|
87
|
+
flwr/client/grpc_rere_client/connection.py,sha256=__WQgS02WgOXopDnuighOzWLBVQY-0YCGtVjFhLaTZQ,13603
|
88
88
|
flwr/client/grpc_rere_client/grpc_adapter.py,sha256=dLGB5GriszAmtgvuFGuz_F7rIwpzLfDxhJ7T3Un-Ce0,6694
|
89
89
|
flwr/client/message_handler/__init__.py,sha256=0lyljDVqre3WljiZbPcwCCf8GiIaSVI_yo_ylEyPwSE,719
|
90
90
|
flwr/client/message_handler/message_handler.py,sha256=X9SXX6et97Lw9_DGD93HKsEBGNjXClcFgc_5aLK0oiU,6541
|
@@ -98,7 +98,7 @@ flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=aKqjZCrikF73y3E-7h40
|
|
98
98
|
flwr/client/mod/utils.py,sha256=FUgD2TfcWqSeF6jUKZ4i6Ke56U4Nrv85AeVb93s6R9g,1201
|
99
99
|
flwr/client/numpy_client.py,sha256=Qq6ghsIAop2slKqAfgiI5NiHJ4LIxGmrik3Ror4_XVc,9581
|
100
100
|
flwr/client/rest_client/__init__.py,sha256=MBiuK62hj439m9rtwSwI184Hth6Tt5GbmpNMyl3zkZY,735
|
101
|
-
flwr/client/rest_client/connection.py,sha256=
|
101
|
+
flwr/client/rest_client/connection.py,sha256=nGWgvOEvXpGcpu6G2Uxz34qOnB1NutIVi4tDMgvvJ1E,15738
|
102
102
|
flwr/client/run_info_store.py,sha256=MaJ3UQ-07hWtK67wnWu0zR29jrk0fsfgJX506dvEOfE,4042
|
103
103
|
flwr/client/typing.py,sha256=Jw3rawDzI_-ZDcRmEQcs5gZModY7oeQlEeltYsdOhlU,1048
|
104
104
|
flwr/clientapp/__init__.py,sha256=zGW4z49Ojzoi1hDiRC7kyhLjijUilc6fqHhtM_ATRVA,719
|
@@ -365,12 +365,12 @@ flwr/supernode/nodestate/in_memory_nodestate.py,sha256=LF3AbaW0bcZHY5yKWwUJSU2RZ
|
|
365
365
|
flwr/supernode/nodestate/nodestate.py,sha256=kkGFxYnLIwT4-UmlPnf6HvAUpPey2urUNrweGybAIY4,6398
|
366
366
|
flwr/supernode/nodestate/nodestate_factory.py,sha256=UYTDCcwK_baHUmkzkJDxL0UEqvtTfOMlQRrROMCd0Xo,1430
|
367
367
|
flwr/supernode/runtime/__init__.py,sha256=JQdqd2EMTn-ORMeTvewYYh52ls0YKP68jrps1qioxu4,718
|
368
|
-
flwr/supernode/runtime/run_clientapp.py,sha256=
|
368
|
+
flwr/supernode/runtime/run_clientapp.py,sha256=woAO8rXclt5eZeNHokhBChgxMf-TAzqWnHCkoiSsLVs,10765
|
369
369
|
flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca8gxdEo,717
|
370
370
|
flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
|
371
371
|
flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=SOx719uqdkIHryBri-XpuYpALQE__hxNtDjSAdeUtug,10059
|
372
|
-
flwr/supernode/start_client_internal.py,sha256=
|
373
|
-
flwr_nightly-1.20.0.
|
374
|
-
flwr_nightly-1.20.0.
|
375
|
-
flwr_nightly-1.20.0.
|
376
|
-
flwr_nightly-1.20.0.
|
372
|
+
flwr/supernode/start_client_internal.py,sha256=Sa9HT-YrS_RPkY44Dv6X-EZOX8HGupJyS4EY_e0x8u8,21367
|
373
|
+
flwr_nightly-1.20.0.dev20250721.dist-info/METADATA,sha256=leBKwaZsOax_yJrfB6silj5VVDX6i3Oe_GHSF2D3yV0,15966
|
374
|
+
flwr_nightly-1.20.0.dev20250721.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
375
|
+
flwr_nightly-1.20.0.dev20250721.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
|
376
|
+
flwr_nightly-1.20.0.dev20250721.dist-info/RECORD,,
|
{flwr_nightly-1.20.0.dev20250720.dist-info → flwr_nightly-1.20.0.dev20250721.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|