flwr-nightly 1.19.0.dev20250527__py3-none-any.whl → 1.19.0.dev20250529__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/cli/log.py +3 -3
- flwr/cli/login/login.py +3 -7
- flwr/cli/ls.py +3 -3
- flwr/cli/run/run.py +2 -6
- flwr/cli/stop.py +2 -2
- flwr/cli/utils.py +5 -4
- flwr/client/grpc_rere_client/connection.py +2 -0
- flwr/client/message_handler/message_handler.py +1 -1
- flwr/client/mod/comms_mods.py +36 -17
- flwr/common/auth_plugin/auth_plugin.py +8 -2
- flwr/common/inflatable.py +33 -2
- flwr/common/message.py +11 -0
- flwr/common/record/array.py +38 -1
- flwr/common/record/arrayrecord.py +34 -0
- flwr/common/serde.py +6 -1
- flwr/proto/fleet_pb2.py +16 -16
- flwr/proto/fleet_pb2.pyi +5 -5
- flwr/proto/message_pb2.py +10 -10
- flwr/proto/message_pb2.pyi +4 -4
- flwr/proto/serverappio_pb2.py +26 -26
- flwr/proto/serverappio_pb2.pyi +5 -5
- flwr/server/app.py +52 -56
- flwr/server/grid/grpc_grid.py +2 -1
- flwr/server/grid/inmemory_grid.py +5 -4
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +1 -0
- flwr/server/superlink/fleet/message_handler/message_handler.py +11 -3
- flwr/server/superlink/fleet/rest_rere/rest_api.py +3 -1
- flwr/server/superlink/fleet/vce/vce_api.py +3 -0
- flwr/server/superlink/linkstate/in_memory_linkstate.py +14 -25
- flwr/server/superlink/linkstate/linkstate.py +9 -10
- flwr/server/superlink/linkstate/sqlite_linkstate.py +11 -21
- flwr/server/superlink/linkstate/utils.py +23 -23
- flwr/server/superlink/serverappio/serverappio_servicer.py +16 -11
- flwr/server/superlink/utils.py +29 -0
- flwr/server/utils/validator.py +2 -2
- flwr/supercore/object_store/in_memory_object_store.py +30 -4
- flwr/supercore/object_store/object_store.py +48 -1
- flwr/superexec/exec_servicer.py +1 -2
- {flwr_nightly-1.19.0.dev20250527.dist-info → flwr_nightly-1.19.0.dev20250529.dist-info}/METADATA +1 -1
- {flwr_nightly-1.19.0.dev20250527.dist-info → flwr_nightly-1.19.0.dev20250529.dist-info}/RECORD +42 -42
- {flwr_nightly-1.19.0.dev20250527.dist-info → flwr_nightly-1.19.0.dev20250529.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.19.0.dev20250527.dist-info → flwr_nightly-1.19.0.dev20250529.dist-info}/entry_points.txt +0 -0
@@ -24,7 +24,6 @@ import time
|
|
24
24
|
from collections.abc import Sequence
|
25
25
|
from logging import DEBUG, ERROR, WARNING
|
26
26
|
from typing import Any, Optional, Union, cast
|
27
|
-
from uuid import UUID, uuid4
|
28
27
|
|
29
28
|
from flwr.common import Context, Message, Metadata, log, now
|
30
29
|
from flwr.common.constant import (
|
@@ -251,19 +250,15 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
251
250
|
|
252
251
|
return result
|
253
252
|
|
254
|
-
def store_message_ins(self, message: Message) -> Optional[
|
253
|
+
def store_message_ins(self, message: Message) -> Optional[str]:
|
255
254
|
"""Store one Message."""
|
256
255
|
# Validate message
|
257
256
|
errors = validate_message(message=message, is_reply_message=False)
|
258
257
|
if any(errors):
|
259
258
|
log(ERROR, errors)
|
260
259
|
return None
|
261
|
-
# Create message_id
|
262
|
-
message_id = uuid4()
|
263
260
|
|
264
261
|
# Store Message
|
265
|
-
# pylint: disable-next=W0212
|
266
|
-
message.metadata._message_id = str(message_id) # type: ignore
|
267
262
|
data = (message_to_dict(message),)
|
268
263
|
|
269
264
|
# Convert values from uint64 to sint64 for SQLite
|
@@ -303,7 +298,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
303
298
|
# This may need to be changed in the future version with more integrity checks.
|
304
299
|
self.query(query, data)
|
305
300
|
|
306
|
-
return message_id
|
301
|
+
return message.metadata.message_id
|
307
302
|
|
308
303
|
def get_message_ins(self, node_id: int, limit: Optional[int]) -> list[Message]:
|
309
304
|
"""Get all Messages that have not been delivered yet."""
|
@@ -366,7 +361,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
366
361
|
|
367
362
|
return result
|
368
363
|
|
369
|
-
def store_message_res(self, message: Message) -> Optional[
|
364
|
+
def store_message_res(self, message: Message) -> Optional[str]:
|
370
365
|
"""Store one Message."""
|
371
366
|
# Validate message
|
372
367
|
errors = validate_message(message=message, is_reply_message=True)
|
@@ -418,12 +413,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
418
413
|
)
|
419
414
|
return None
|
420
415
|
|
421
|
-
# Create message_id
|
422
|
-
message_id = uuid4()
|
423
|
-
|
424
416
|
# Store Message
|
425
|
-
# pylint: disable-next=W0212
|
426
|
-
message.metadata._message_id = str(message_id) # type: ignore
|
427
417
|
data = (message_to_dict(message),)
|
428
418
|
|
429
419
|
# Convert values from uint64 to sint64 for SQLite
|
@@ -442,12 +432,12 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
442
432
|
log(ERROR, "`run` is invalid")
|
443
433
|
return None
|
444
434
|
|
445
|
-
return message_id
|
435
|
+
return message.metadata.message_id
|
446
436
|
|
447
|
-
def get_message_res(self, message_ids: set[
|
437
|
+
def get_message_res(self, message_ids: set[str]) -> list[Message]:
|
448
438
|
"""Get reply Messages for the given Message IDs."""
|
449
439
|
# pylint: disable-msg=too-many-locals
|
450
|
-
ret: dict[
|
440
|
+
ret: dict[str, Message] = {}
|
451
441
|
|
452
442
|
# Verify Message IDs
|
453
443
|
current = time.time()
|
@@ -457,12 +447,12 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
457
447
|
WHERE message_id IN ({",".join(["?"] * len(message_ids))});
|
458
448
|
"""
|
459
449
|
rows = self.query(query, tuple(str(message_id) for message_id in message_ids))
|
460
|
-
found_message_ins_dict: dict[
|
450
|
+
found_message_ins_dict: dict[str, Message] = {}
|
461
451
|
for row in rows:
|
462
452
|
convert_sint64_values_in_dict_to_uint64(
|
463
453
|
row, ["run_id", "src_node_id", "dst_node_id"]
|
464
454
|
)
|
465
|
-
found_message_ins_dict[
|
455
|
+
found_message_ins_dict[row["message_id"]] = dict_to_message(row)
|
466
456
|
|
467
457
|
ret = verify_message_ids(
|
468
458
|
inquired_message_ids=message_ids,
|
@@ -551,7 +541,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
551
541
|
result: dict[str, int] = rows[0]
|
552
542
|
return result["num"]
|
553
543
|
|
554
|
-
def delete_messages(self, message_ins_ids: set[
|
544
|
+
def delete_messages(self, message_ins_ids: set[str]) -> None:
|
555
545
|
"""Delete a Message and its reply based on provided Message IDs."""
|
556
546
|
if not message_ins_ids:
|
557
547
|
return
|
@@ -577,7 +567,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
577
567
|
self.conn.execute(query_1, data)
|
578
568
|
self.conn.execute(query_2, data)
|
579
569
|
|
580
|
-
def get_message_ids_from_run_id(self, run_id: int) -> set[
|
570
|
+
def get_message_ids_from_run_id(self, run_id: int) -> set[str]:
|
581
571
|
"""Get all instruction Message IDs for the given run_id."""
|
582
572
|
if self.conn is None:
|
583
573
|
raise AttributeError("LinkState not initialized")
|
@@ -594,7 +584,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
|
|
594
584
|
with self.conn:
|
595
585
|
rows = self.conn.execute(query, data).fetchall()
|
596
586
|
|
597
|
-
return {
|
587
|
+
return {row["message_id"] for row in rows}
|
598
588
|
|
599
589
|
def create_node(self, heartbeat_interval: float) -> int:
|
600
590
|
"""Create, store in the link state, and return `node_id`."""
|
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
from os import urandom
|
19
19
|
from typing import Optional
|
20
|
-
from uuid import
|
20
|
+
from uuid import uuid4
|
21
21
|
|
22
22
|
from flwr.common import ConfigRecord, Context, Error, Message, Metadata, now, serde
|
23
23
|
from flwr.common.constant import (
|
@@ -273,7 +273,7 @@ def create_message_error_unavailable_res_message(
|
|
273
273
|
)
|
274
274
|
|
275
275
|
|
276
|
-
def create_message_error_unavailable_ins_message(reply_to_message_id:
|
276
|
+
def create_message_error_unavailable_ins_message(reply_to_message_id: str) -> Message:
|
277
277
|
"""Error to indicate that the enquired Message had expired before reply arrived or
|
278
278
|
that it isn't found."""
|
279
279
|
metadata = Metadata(
|
@@ -281,7 +281,7 @@ def create_message_error_unavailable_ins_message(reply_to_message_id: UUID) -> M
|
|
281
281
|
message_id=str(uuid4()),
|
282
282
|
src_node_id=SUPERLINK_NODE_ID,
|
283
283
|
dst_node_id=SUPERLINK_NODE_ID,
|
284
|
-
reply_to_message_id=
|
284
|
+
reply_to_message_id=reply_to_message_id,
|
285
285
|
group_id="", # Unknown
|
286
286
|
message_type=MessageType.SYSTEM,
|
287
287
|
created_at=now().timestamp(),
|
@@ -303,18 +303,18 @@ def message_ttl_has_expired(message_metadata: Metadata, current_time: float) ->
|
|
303
303
|
|
304
304
|
|
305
305
|
def verify_message_ids(
|
306
|
-
inquired_message_ids: set[
|
307
|
-
found_message_ins_dict: dict[
|
306
|
+
inquired_message_ids: set[str],
|
307
|
+
found_message_ins_dict: dict[str, Message],
|
308
308
|
current_time: Optional[float] = None,
|
309
309
|
update_set: bool = True,
|
310
|
-
) -> dict[
|
310
|
+
) -> dict[str, Message]:
|
311
311
|
"""Verify found Messages and generate error Messages for invalid ones.
|
312
312
|
|
313
313
|
Parameters
|
314
314
|
----------
|
315
|
-
inquired_message_ids : set[
|
315
|
+
inquired_message_ids : set[str]
|
316
316
|
Set of Message IDs for which to generate error Message if invalid.
|
317
|
-
found_message_ins_dict : dict[
|
317
|
+
found_message_ins_dict : dict[str, Message]
|
318
318
|
Dictionary containing all found Message indexed by their IDs.
|
319
319
|
current_time : Optional[float] (default: None)
|
320
320
|
The current time to check for expiration. If set to `None`, the current time
|
@@ -325,7 +325,7 @@ def verify_message_ids(
|
|
325
325
|
|
326
326
|
Returns
|
327
327
|
-------
|
328
|
-
dict[
|
328
|
+
dict[str, Message]
|
329
329
|
A dictionary of error Message indexed by the corresponding ID of the message
|
330
330
|
they are a reply of.
|
331
331
|
"""
|
@@ -345,19 +345,19 @@ def verify_message_ids(
|
|
345
345
|
|
346
346
|
|
347
347
|
def verify_found_message_replies(
|
348
|
-
inquired_message_ids: set[
|
349
|
-
found_message_ins_dict: dict[
|
348
|
+
inquired_message_ids: set[str],
|
349
|
+
found_message_ins_dict: dict[str, Message],
|
350
350
|
found_message_res_list: list[Message],
|
351
351
|
current_time: Optional[float] = None,
|
352
352
|
update_set: bool = True,
|
353
|
-
) -> dict[
|
353
|
+
) -> dict[str, Message]:
|
354
354
|
"""Verify found Message replies and generate error Message for invalid ones.
|
355
355
|
|
356
356
|
Parameters
|
357
357
|
----------
|
358
|
-
inquired_message_ids : set[
|
358
|
+
inquired_message_ids : set[str]
|
359
359
|
Set of Message IDs for which to generate error Message if invalid.
|
360
|
-
found_message_ins_dict : dict[
|
360
|
+
found_message_ins_dict : dict[str, Message]
|
361
361
|
Dictionary containing all found instruction Messages indexed by their IDs.
|
362
362
|
found_message_res_list : dict[Message, Message]
|
363
363
|
List of found Message to be verified.
|
@@ -370,13 +370,13 @@ def verify_found_message_replies(
|
|
370
370
|
|
371
371
|
Returns
|
372
372
|
-------
|
373
|
-
dict[
|
373
|
+
dict[str, Message]
|
374
374
|
A dictionary of Message indexed by the corresponding Message ID.
|
375
375
|
"""
|
376
|
-
ret_dict: dict[
|
376
|
+
ret_dict: dict[str, Message] = {}
|
377
377
|
current = current_time if current_time else now().timestamp()
|
378
378
|
for message_res in found_message_res_list:
|
379
|
-
message_ins_id =
|
379
|
+
message_ins_id = message_res.metadata.reply_to_message_id
|
380
380
|
if update_set:
|
381
381
|
inquired_message_ids.remove(message_ins_id)
|
382
382
|
# Check if the reply Message has expired
|
@@ -390,21 +390,21 @@ def verify_found_message_replies(
|
|
390
390
|
|
391
391
|
|
392
392
|
def check_node_availability_for_in_message(
|
393
|
-
inquired_in_message_ids: set[
|
394
|
-
found_in_message_dict: dict[
|
393
|
+
inquired_in_message_ids: set[str],
|
394
|
+
found_in_message_dict: dict[str, Message],
|
395
395
|
node_id_to_online_until: dict[int, float],
|
396
396
|
current_time: Optional[float] = None,
|
397
397
|
update_set: bool = True,
|
398
|
-
) -> dict[
|
398
|
+
) -> dict[str, Message]:
|
399
399
|
"""Check node availability for given Message and generate error reply Message if
|
400
400
|
unavailable. A Message error indicating node unavailability will be generated for
|
401
401
|
each given Message whose destination node is offline or non-existent.
|
402
402
|
|
403
403
|
Parameters
|
404
404
|
----------
|
405
|
-
inquired_in_message_ids : set[
|
405
|
+
inquired_in_message_ids : set[str]
|
406
406
|
Set of Message IDs for which to check destination node availability.
|
407
|
-
found_in_message_dict : dict[
|
407
|
+
found_in_message_dict : dict[str, Message]
|
408
408
|
Dictionary containing all found Message indexed by their IDs.
|
409
409
|
node_id_to_online_until : dict[int, float]
|
410
410
|
Dictionary mapping node IDs to their online-until timestamps.
|
@@ -417,7 +417,7 @@ def check_node_availability_for_in_message(
|
|
417
417
|
|
418
418
|
Returns
|
419
419
|
-------
|
420
|
-
dict[
|
420
|
+
dict[str, Message]
|
421
421
|
A dictionary of error Message indexed by the corresponding Message ID.
|
422
422
|
"""
|
423
423
|
ret_dict = {}
|
@@ -18,7 +18,6 @@
|
|
18
18
|
import threading
|
19
19
|
from logging import DEBUG, INFO
|
20
20
|
from typing import Optional
|
21
|
-
from uuid import UUID
|
22
21
|
|
23
22
|
import grpc
|
24
23
|
|
@@ -81,6 +80,8 @@ from flwr.server.superlink.utils import abort_if
|
|
81
80
|
from flwr.server.utils.validator import validate_message
|
82
81
|
from flwr.supercore.object_store import ObjectStoreFactory
|
83
82
|
|
83
|
+
from ..utils import store_mapping_and_register_objects
|
84
|
+
|
84
85
|
|
85
86
|
class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
86
87
|
"""ServerAppIo API servicer."""
|
@@ -140,7 +141,7 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
140
141
|
request_name="PushMessages",
|
141
142
|
detail="`messages_list` must not be empty",
|
142
143
|
)
|
143
|
-
message_ids: list[Optional[
|
144
|
+
message_ids: list[Optional[str]] = []
|
144
145
|
while request.messages_list:
|
145
146
|
message_proto = request.messages_list.pop(0)
|
146
147
|
message = message_from_proto(message_proto=message_proto)
|
@@ -156,13 +157,20 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
156
157
|
detail="`Message.metadata` has mismatched `run_id`",
|
157
158
|
)
|
158
159
|
# Store
|
159
|
-
message_id: Optional[
|
160
|
+
message_id: Optional[str] = state.store_message_ins(message=message)
|
160
161
|
message_ids.append(message_id)
|
161
162
|
|
163
|
+
# Init store
|
164
|
+
store = self.objectstore_factory.store()
|
165
|
+
|
166
|
+
# Store Message object to descendants mapping and preregister objects
|
167
|
+
objects_to_push = store_mapping_and_register_objects(store, request=request)
|
168
|
+
|
162
169
|
return PushInsMessagesResponse(
|
163
170
|
message_ids=[
|
164
171
|
str(message_id) if message_id else "" for message_id in message_ids
|
165
|
-
]
|
172
|
+
],
|
173
|
+
objects_to_push=objects_to_push,
|
166
174
|
)
|
167
175
|
|
168
176
|
def PullMessages(
|
@@ -182,17 +190,14 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
182
190
|
context,
|
183
191
|
)
|
184
192
|
|
185
|
-
# Convert each message_id str to UUID
|
186
|
-
message_ids: set[UUID] = {
|
187
|
-
UUID(message_id) for message_id in request.message_ids
|
188
|
-
}
|
189
|
-
|
190
193
|
# Read from state
|
191
|
-
messages_res: list[Message] = state.get_message_res(
|
194
|
+
messages_res: list[Message] = state.get_message_res(
|
195
|
+
message_ids=set(request.message_ids)
|
196
|
+
)
|
192
197
|
|
193
198
|
# Delete the instruction Messages and their replies if found
|
194
199
|
message_ins_ids_to_delete = {
|
195
|
-
|
200
|
+
msg_res.metadata.reply_to_message_id for msg_res in messages_res
|
196
201
|
}
|
197
202
|
|
198
203
|
state.delete_messages(message_ins_ids=message_ins_ids_to_delete)
|
flwr/server/superlink/utils.py
CHANGED
@@ -21,7 +21,11 @@ import grpc
|
|
21
21
|
|
22
22
|
from flwr.common.constant import Status, SubStatus
|
23
23
|
from flwr.common.typing import RunStatus
|
24
|
+
from flwr.proto.fleet_pb2 import PushMessagesRequest # pylint: disable=E0611
|
25
|
+
from flwr.proto.message_pb2 import ObjectIDs # pylint: disable=E0611
|
26
|
+
from flwr.proto.serverappio_pb2 import PushInsMessagesRequest # pylint: disable=E0611
|
24
27
|
from flwr.server.superlink.linkstate import LinkState
|
28
|
+
from flwr.supercore.object_store import ObjectStore
|
25
29
|
|
26
30
|
_STATUS_TO_MSG = {
|
27
31
|
Status.PENDING: "Run is pending.",
|
@@ -63,3 +67,28 @@ def abort_if(
|
|
63
67
|
"""Abort context if status of the provided `run_id` is in `abort_status_list`."""
|
64
68
|
msg = check_abort(run_id, abort_status_list, state)
|
65
69
|
abort_grpc_context(msg, context)
|
70
|
+
|
71
|
+
|
72
|
+
def store_mapping_and_register_objects(
|
73
|
+
store: ObjectStore, request: Union[PushInsMessagesRequest, PushMessagesRequest]
|
74
|
+
) -> dict[str, ObjectIDs]:
|
75
|
+
"""Store Message object to descendants mapping and preregister objects."""
|
76
|
+
objects_to_push: dict[str, ObjectIDs] = {}
|
77
|
+
for (
|
78
|
+
message_obj_id,
|
79
|
+
descendant_obj_ids,
|
80
|
+
) in request.msg_to_descendant_mapping.items():
|
81
|
+
descendants = list(descendant_obj_ids.object_ids)
|
82
|
+
# Store mapping
|
83
|
+
store.set_message_descendant_ids(
|
84
|
+
msg_object_id=message_obj_id, descendant_ids=descendants
|
85
|
+
)
|
86
|
+
|
87
|
+
# Preregister
|
88
|
+
object_ids_just_registered = store.preregister(descendants + [message_obj_id])
|
89
|
+
# Keep track of objects that need to be pushed
|
90
|
+
objects_to_push[message_obj_id] = ObjectIDs(
|
91
|
+
object_ids=object_ids_just_registered
|
92
|
+
)
|
93
|
+
|
94
|
+
return objects_to_push
|
flwr/server/utils/validator.py
CHANGED
@@ -27,8 +27,8 @@ def validate_message(message: Message, is_reply_message: bool) -> list[str]:
|
|
27
27
|
validation_errors = []
|
28
28
|
metadata = message.metadata
|
29
29
|
|
30
|
-
if metadata.message_id
|
31
|
-
validation_errors.append("
|
30
|
+
if metadata.message_id == "":
|
31
|
+
validation_errors.append("empty `metadata.message_id`")
|
32
32
|
|
33
33
|
# Created/delivered/TTL/Pushed
|
34
34
|
if (
|
@@ -28,12 +28,27 @@ class InMemoryObjectStore(ObjectStore):
|
|
28
28
|
def __init__(self, verify: bool = True) -> None:
|
29
29
|
self.verify = verify
|
30
30
|
self.store: dict[str, bytes] = {}
|
31
|
+
# Mapping the Object ID of a message to the list of children object IDs
|
32
|
+
self.msg_children_objects_mapping: dict[str, list[str]] = {}
|
33
|
+
|
34
|
+
def preregister(self, object_ids: list[str]) -> list[str]:
|
35
|
+
"""Identify and preregister missing objects."""
|
36
|
+
new_objects = []
|
37
|
+
for obj_id in object_ids:
|
38
|
+
# Verify object ID format (must be a valid sha256 hash)
|
39
|
+
if not is_valid_sha256_hash(obj_id):
|
40
|
+
raise ValueError(f"Invalid object ID format: {obj_id}")
|
41
|
+
if obj_id not in self.store:
|
42
|
+
self.store[obj_id] = b""
|
43
|
+
new_objects.append(obj_id)
|
44
|
+
|
45
|
+
return new_objects
|
31
46
|
|
32
47
|
def put(self, object_id: str, object_content: bytes) -> None:
|
33
48
|
"""Put an object into the store."""
|
34
|
-
#
|
35
|
-
if not
|
36
|
-
raise
|
49
|
+
# Only allow adding the object if it has been preregistered
|
50
|
+
if object_id not in self.store:
|
51
|
+
raise KeyError(f"Object with id {object_id} was not preregistered.")
|
37
52
|
|
38
53
|
# Verify object_id and object_content match
|
39
54
|
if self.verify:
|
@@ -42,11 +57,22 @@ class InMemoryObjectStore(ObjectStore):
|
|
42
57
|
raise ValueError(f"Object ID {object_id} does not match content hash")
|
43
58
|
|
44
59
|
# Return if object is already present in the store
|
45
|
-
if object_id
|
60
|
+
if self.store[object_id] != b"":
|
46
61
|
return
|
47
62
|
|
48
63
|
self.store[object_id] = object_content
|
49
64
|
|
65
|
+
def set_message_descendant_ids(
|
66
|
+
self, msg_object_id: str, descendant_ids: list[str]
|
67
|
+
) -> None:
|
68
|
+
"""Store the mapping from a ``Message`` object ID to the object IDs of its
|
69
|
+
descendants."""
|
70
|
+
self.msg_children_objects_mapping[msg_object_id] = descendant_ids
|
71
|
+
|
72
|
+
def get_message_descendant_ids(self, msg_object_id: str) -> list[str]:
|
73
|
+
"""Retrieve the object IDs of all descendants of a given Message."""
|
74
|
+
return self.msg_children_objects_mapping[msg_object_id]
|
75
|
+
|
50
76
|
def get(self, object_id: str) -> Optional[bytes]:
|
51
77
|
"""Get an object from the store."""
|
52
78
|
return self.store.get(object_id)
|
@@ -26,6 +26,23 @@ class ObjectStore(abc.ABC):
|
|
26
26
|
delete objects identified by object IDs.
|
27
27
|
"""
|
28
28
|
|
29
|
+
@abc.abstractmethod
|
30
|
+
def preregister(self, object_ids: list[str]) -> list[str]:
|
31
|
+
"""Identify and preregister missing objects in the `ObjectStore`.
|
32
|
+
|
33
|
+
Parameters
|
34
|
+
----------
|
35
|
+
object_ids : list[str]
|
36
|
+
A list of object IDs to check against the store. Any object ID not already
|
37
|
+
present will be preregistered.
|
38
|
+
|
39
|
+
Returns
|
40
|
+
-------
|
41
|
+
list[str]
|
42
|
+
A list of object IDs that were not present in the `ObjectStore` and have now
|
43
|
+
been preregistered.
|
44
|
+
"""
|
45
|
+
|
29
46
|
@abc.abstractmethod
|
30
47
|
def put(self, object_id: str, object_content: bytes) -> None:
|
31
48
|
"""Put an object into the store.
|
@@ -33,7 +50,7 @@ class ObjectStore(abc.ABC):
|
|
33
50
|
Parameters
|
34
51
|
----------
|
35
52
|
object_id : str
|
36
|
-
The object_id under which to store the object.
|
53
|
+
The object_id under which to store the object. Must be preregistered.
|
37
54
|
object_content : bytes
|
38
55
|
The deflated object to store.
|
39
56
|
"""
|
@@ -70,6 +87,36 @@ class ObjectStore(abc.ABC):
|
|
70
87
|
This method should remove all objects from the store.
|
71
88
|
"""
|
72
89
|
|
90
|
+
@abc.abstractmethod
|
91
|
+
def set_message_descendant_ids(
|
92
|
+
self, msg_object_id: str, descendant_ids: list[str]
|
93
|
+
) -> None:
|
94
|
+
"""Store the mapping from a ``Message`` object ID to the object IDs of its
|
95
|
+
descendants.
|
96
|
+
|
97
|
+
Parameters
|
98
|
+
----------
|
99
|
+
msg_object_id : str
|
100
|
+
The object ID of the ``Message``.
|
101
|
+
descendant_ids : list[str]
|
102
|
+
A list of object IDs representing all descendant objects of the ``Message``.
|
103
|
+
"""
|
104
|
+
|
105
|
+
@abc.abstractmethod
|
106
|
+
def get_message_descendant_ids(self, msg_object_id: str) -> list[str]:
|
107
|
+
"""Retrieve the object IDs of all descendants of a given ``Message``.
|
108
|
+
|
109
|
+
Parameters
|
110
|
+
----------
|
111
|
+
msg_object_id : str
|
112
|
+
The object ID of the ``Message``.
|
113
|
+
|
114
|
+
Returns
|
115
|
+
-------
|
116
|
+
list[str]
|
117
|
+
A list of object IDs of all descendant objects of the ``Message``.
|
118
|
+
"""
|
119
|
+
|
73
120
|
@abc.abstractmethod
|
74
121
|
def __contains__(self, object_id: str) -> bool:
|
75
122
|
"""Check if an object_id is in the store.
|
flwr/superexec/exec_servicer.py
CHANGED
@@ -19,7 +19,6 @@ import time
|
|
19
19
|
from collections.abc import Generator
|
20
20
|
from logging import ERROR, INFO
|
21
21
|
from typing import Any, Optional
|
22
|
-
from uuid import UUID
|
23
22
|
|
24
23
|
import grpc
|
25
24
|
|
@@ -163,7 +162,7 @@ class ExecServicer(exec_pb2_grpc.ExecServicer):
|
|
163
162
|
)
|
164
163
|
|
165
164
|
if update_success:
|
166
|
-
message_ids: set[
|
165
|
+
message_ids: set[str] = state.get_message_ids_from_run_id(request.run_id)
|
167
166
|
|
168
167
|
# Delete Messages and their replies for the `run_id`
|
169
168
|
state.delete_messages(message_ids)
|
{flwr_nightly-1.19.0.dev20250527.dist-info → flwr_nightly-1.19.0.dev20250529.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: flwr-nightly
|
3
|
-
Version: 1.19.0.
|
3
|
+
Version: 1.19.0.dev20250529
|
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
|