flwr-nightly 1.19.0.dev20250602__py3-none-any.whl → 1.19.0.dev20250604__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_rere_client/connection.py +52 -24
- flwr/common/inflatable.py +8 -2
- flwr/common/inflatable_grpc_utils.py +12 -6
- flwr/common/record/metricrecord.py +1 -1
- flwr/common/retry_invoker.py +5 -1
- flwr/proto/message_pb2.py +2 -2
- flwr/proto/message_pb2.pyi +7 -1
- flwr/server/grid/grpc_grid.py +73 -34
- flwr/server/superlink/ffs/__init__.py +2 -0
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +62 -5
- flwr/server/superlink/serverappio/serverappio_servicer.py +58 -3
- flwr/supernode/cli/flower_supernode.py +1 -2
- flwr/supernode/cli/flwr_clientapp.py +8 -0
- flwr/supernode/runtime/run_clientapp.py +22 -1
- flwr/supernode/start_client_internal.py +83 -100
- {flwr_nightly-1.19.0.dev20250602.dist-info → flwr_nightly-1.19.0.dev20250604.dist-info}/METADATA +1 -1
- {flwr_nightly-1.19.0.dev20250602.dist-info → flwr_nightly-1.19.0.dev20250604.dist-info}/RECORD +19 -19
- {flwr_nightly-1.19.0.dev20250602.dist-info → flwr_nightly-1.19.0.dev20250604.dist-info}/WHEEL +0 -0
- {flwr_nightly-1.19.0.dev20250602.dist-info → flwr_nightly-1.19.0.dev20250604.dist-info}/entry_points.txt +0 -0
@@ -14,11 +14,10 @@
|
|
14
14
|
# ==============================================================================
|
15
15
|
"""Contextmanager for a gRPC request-response channel to the Flower server."""
|
16
16
|
|
17
|
-
|
18
17
|
from collections.abc import Iterator, Sequence
|
19
18
|
from contextlib import contextmanager
|
20
19
|
from copy import copy
|
21
|
-
from logging import ERROR
|
20
|
+
from logging import DEBUG, ERROR
|
22
21
|
from pathlib import Path
|
23
22
|
from typing import Callable, Optional, Union, cast
|
24
23
|
|
@@ -31,13 +30,17 @@ from flwr.common import GRPC_MAX_MESSAGE_LENGTH
|
|
31
30
|
from flwr.common.constant import HEARTBEAT_CALL_TIMEOUT, HEARTBEAT_DEFAULT_INTERVAL
|
32
31
|
from flwr.common.grpc import create_channel, on_channel_state_change
|
33
32
|
from flwr.common.heartbeat import HeartbeatSender
|
33
|
+
from flwr.common.inflatable_grpc_utils import (
|
34
|
+
pull_object_from_servicer,
|
35
|
+
push_object_to_servicer,
|
36
|
+
)
|
34
37
|
from flwr.common.logger import log
|
35
|
-
from flwr.common.message import Message
|
36
|
-
from flwr.common.retry_invoker import RetryInvoker
|
38
|
+
from flwr.common.message import Message, get_message_to_descendant_id_mapping
|
39
|
+
from flwr.common.retry_invoker import RetryInvoker, _wrap_stub
|
37
40
|
from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
|
38
41
|
generate_key_pairs,
|
39
42
|
)
|
40
|
-
from flwr.common.serde import
|
43
|
+
from flwr.common.serde import message_to_proto, run_from_proto
|
41
44
|
from flwr.common.typing import Fab, Run, RunNotRunningException
|
42
45
|
from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
|
43
46
|
from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
@@ -46,6 +49,7 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
|
|
46
49
|
PullMessagesRequest,
|
47
50
|
PullMessagesResponse,
|
48
51
|
PushMessagesRequest,
|
52
|
+
PushMessagesResponse,
|
49
53
|
)
|
50
54
|
from flwr.proto.fleet_pb2_grpc import FleetStub # pylint: disable=E0611
|
51
55
|
from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
|
@@ -159,6 +163,8 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
159
163
|
# If the status code is PERMISSION_DENIED, additionally raise RunNotRunningException
|
160
164
|
retry_invoker.should_giveup = _should_giveup_fn
|
161
165
|
|
166
|
+
# Wrap stub
|
167
|
+
_wrap_stub(stub, retry_invoker)
|
162
168
|
###########################################################################
|
163
169
|
# send_node_heartbeat/create_node/delete_node/receive/send/get_run functions
|
164
170
|
###########################################################################
|
@@ -203,10 +209,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
203
209
|
create_node_request = CreateNodeRequest(
|
204
210
|
heartbeat_interval=HEARTBEAT_DEFAULT_INTERVAL
|
205
211
|
)
|
206
|
-
create_node_response =
|
207
|
-
stub.CreateNode,
|
208
|
-
request=create_node_request,
|
209
|
-
)
|
212
|
+
create_node_response = stub.CreateNode(request=create_node_request)
|
210
213
|
|
211
214
|
# Remember the node and start the heartbeat sender
|
212
215
|
nonlocal node
|
@@ -227,7 +230,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
227
230
|
|
228
231
|
# Call FleetAPI
|
229
232
|
delete_node_request = DeleteNodeRequest(node=node)
|
230
|
-
|
233
|
+
stub.DeleteNode(request=delete_node_request)
|
231
234
|
|
232
235
|
# Cleanup
|
233
236
|
node = None
|
@@ -241,9 +244,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
241
244
|
|
242
245
|
# Request instructions (message) from server
|
243
246
|
request = PullMessagesRequest(node=node)
|
244
|
-
response: PullMessagesResponse =
|
245
|
-
stub.PullMessages, request=request
|
246
|
-
)
|
247
|
+
response: PullMessagesResponse = stub.PullMessages(request=request)
|
247
248
|
|
248
249
|
# Get the current Messages
|
249
250
|
message_proto = (
|
@@ -257,7 +258,24 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
257
258
|
message_proto = None
|
258
259
|
|
259
260
|
# Construct the Message
|
260
|
-
in_message =
|
261
|
+
in_message: Optional[Message] = None
|
262
|
+
|
263
|
+
if message_proto:
|
264
|
+
in_message = cast(
|
265
|
+
Message,
|
266
|
+
pull_object_from_servicer(
|
267
|
+
object_id=message_proto.metadata.message_id,
|
268
|
+
stub=stub,
|
269
|
+
node=node,
|
270
|
+
run_id=message_proto.metadata.run_id,
|
271
|
+
),
|
272
|
+
)
|
273
|
+
|
274
|
+
if in_message:
|
275
|
+
# The deflated message doesn't contain the message_id (its own object_id)
|
276
|
+
# Inject
|
277
|
+
# pylint: disable-next=W0212
|
278
|
+
in_message.metadata._message_id = message_proto.metadata.message_id # type: ignore
|
261
279
|
|
262
280
|
# Remember `metadata` of the in message
|
263
281
|
nonlocal metadata
|
@@ -288,8 +306,24 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
288
306
|
|
289
307
|
# Serialize Message
|
290
308
|
message_proto = message_to_proto(message=message)
|
291
|
-
|
292
|
-
|
309
|
+
descendants_mapping = get_message_to_descendant_id_mapping(message)
|
310
|
+
request = PushMessagesRequest(
|
311
|
+
node=node,
|
312
|
+
messages_list=[message_proto],
|
313
|
+
msg_to_descendant_mapping=descendants_mapping,
|
314
|
+
)
|
315
|
+
response: PushMessagesResponse = stub.PushMessages(request=request)
|
316
|
+
|
317
|
+
if response.objects_to_push:
|
318
|
+
objs_to_push = set(response.objects_to_push[message.object_id].object_ids)
|
319
|
+
push_object_to_servicer(
|
320
|
+
message,
|
321
|
+
stub,
|
322
|
+
node,
|
323
|
+
run_id=message.metadata.run_id,
|
324
|
+
object_ids_to_push=objs_to_push,
|
325
|
+
)
|
326
|
+
log(DEBUG, "Pushed %s objects to servicer.", len(objs_to_push))
|
293
327
|
|
294
328
|
# Cleanup
|
295
329
|
metadata = None
|
@@ -297,10 +331,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
297
331
|
def get_run(run_id: int) -> Run:
|
298
332
|
# Call FleetAPI
|
299
333
|
get_run_request = GetRunRequest(node=node, run_id=run_id)
|
300
|
-
get_run_response: GetRunResponse =
|
301
|
-
stub.GetRun,
|
302
|
-
request=get_run_request,
|
303
|
-
)
|
334
|
+
get_run_response: GetRunResponse = stub.GetRun(request=get_run_request)
|
304
335
|
|
305
336
|
# Return fab_id and fab_version
|
306
337
|
return run_from_proto(get_run_response.run)
|
@@ -308,10 +339,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
|
|
308
339
|
def get_fab(fab_hash: str, run_id: int) -> Fab:
|
309
340
|
# Call FleetAPI
|
310
341
|
get_fab_request = GetFabRequest(node=node, hash_str=fab_hash, run_id=run_id)
|
311
|
-
get_fab_response: GetFabResponse =
|
312
|
-
stub.GetFab,
|
313
|
-
request=get_fab_request,
|
314
|
-
)
|
342
|
+
get_fab_response: GetFabResponse = stub.GetFab(request=get_fab_request)
|
315
343
|
|
316
344
|
return Fab(get_fab_response.fab.hash_str, get_fab_response.fab.content)
|
317
345
|
|
flwr/common/inflatable.py
CHANGED
@@ -18,9 +18,11 @@
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
20
|
import hashlib
|
21
|
+
from logging import ERROR
|
21
22
|
from typing import TypeVar, cast
|
22
23
|
|
23
24
|
from .constant import HEAD_BODY_DIVIDER, HEAD_VALUE_DIVIDER
|
25
|
+
from .logger import log
|
24
26
|
|
25
27
|
|
26
28
|
class InflatableObject:
|
@@ -163,8 +165,12 @@ def get_object_body_len_from_object_content(object_content: bytes) -> int:
|
|
163
165
|
|
164
166
|
def check_body_len_consistency(object_content: bytes) -> bool:
|
165
167
|
"""Check that the object body is of length as specified in the head."""
|
166
|
-
|
167
|
-
|
168
|
+
try:
|
169
|
+
body_len = get_object_body_len_from_object_content(object_content)
|
170
|
+
return body_len == len(_get_object_body(object_content))
|
171
|
+
except ValueError:
|
172
|
+
log(ERROR, "Object content does match the expected format.")
|
173
|
+
return False
|
168
174
|
|
169
175
|
|
170
176
|
def get_object_head_values_from_object_content(
|
@@ -15,8 +15,10 @@
|
|
15
15
|
"""InflatableObject utils."""
|
16
16
|
|
17
17
|
|
18
|
+
from time import sleep
|
18
19
|
from typing import Optional, Union
|
19
20
|
|
21
|
+
from flwr.client.grpc_rere_client.grpc_adapter import GrpcAdapter
|
20
22
|
from flwr.proto.fleet_pb2_grpc import FleetStub # pylint: disable=E0611
|
21
23
|
from flwr.proto.message_pb2 import ( # pylint: disable=E0611
|
22
24
|
PullObjectRequest,
|
@@ -48,7 +50,7 @@ inflatable_class_registry: dict[str, type[InflatableObject]] = {
|
|
48
50
|
|
49
51
|
def push_object_to_servicer(
|
50
52
|
obj: InflatableObject,
|
51
|
-
stub: Union[FleetStub, ServerAppIoStub],
|
53
|
+
stub: Union[FleetStub, ServerAppIoStub, GrpcAdapter],
|
52
54
|
node: Node,
|
53
55
|
run_id: int,
|
54
56
|
object_ids_to_push: Optional[set[str]] = None,
|
@@ -87,16 +89,20 @@ def push_object_to_servicer(
|
|
87
89
|
|
88
90
|
def pull_object_from_servicer(
|
89
91
|
object_id: str,
|
90
|
-
stub: Union[FleetStub, ServerAppIoStub],
|
92
|
+
stub: Union[FleetStub, ServerAppIoStub, GrpcAdapter],
|
91
93
|
node: Node,
|
92
94
|
run_id: int,
|
93
95
|
) -> InflatableObject:
|
94
96
|
"""Recursively inflate an object by pulling it from the servicer."""
|
95
97
|
# Pull object
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
object_available = False
|
99
|
+
while not object_available:
|
100
|
+
object_proto: PullObjectResponse = stub.PullObject(
|
101
|
+
PullObjectRequest(node=node, run_id=run_id, object_id=object_id)
|
102
|
+
)
|
103
|
+
object_available = object_proto.object_available
|
104
|
+
object_content = object_proto.object_content
|
105
|
+
sleep(0.1)
|
100
106
|
|
101
107
|
# Extract object class and object_ids of children
|
102
108
|
obj_type, children_obj_ids, _ = get_object_head_values_from_object_content(
|
@@ -180,7 +180,7 @@ class MetricRecord(TypedDict[str, MetricRecordValues], InflatableObject):
|
|
180
180
|
MetricRecord
|
181
181
|
The inflated MetricRecord.
|
182
182
|
"""
|
183
|
-
if children
|
183
|
+
if children:
|
184
184
|
raise ValueError("`MetricRecord` objects do not have children.")
|
185
185
|
|
186
186
|
obj_body = get_object_body(object_content, cls)
|
flwr/common/retry_invoker.py
CHANGED
@@ -25,10 +25,12 @@ from typing import Any, Callable, Optional, Union, cast
|
|
25
25
|
|
26
26
|
import grpc
|
27
27
|
|
28
|
+
from flwr.client.grpc_rere_client.grpc_adapter import GrpcAdapter
|
28
29
|
from flwr.common.constant import MAX_RETRY_DELAY
|
29
30
|
from flwr.common.logger import log
|
30
31
|
from flwr.common.typing import RunNotRunningException
|
31
32
|
from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
|
33
|
+
from flwr.proto.fleet_pb2_grpc import FleetStub
|
32
34
|
from flwr.proto.serverappio_pb2_grpc import ServerAppIoStub
|
33
35
|
from flwr.proto.simulationio_pb2_grpc import SimulationIoStub
|
34
36
|
|
@@ -366,7 +368,9 @@ def _make_simple_grpc_retry_invoker() -> RetryInvoker:
|
|
366
368
|
|
367
369
|
|
368
370
|
def _wrap_stub(
|
369
|
-
stub: Union[
|
371
|
+
stub: Union[
|
372
|
+
ServerAppIoStub, ClientAppIoStub, SimulationIoStub, FleetStub, GrpcAdapter
|
373
|
+
],
|
370
374
|
retry_invoker: RetryInvoker,
|
371
375
|
) -> None:
|
372
376
|
"""Wrap a gRPC stub with a retry invoker."""
|
flwr/proto/message_pb2.py
CHANGED
@@ -18,7 +18,7 @@ from flwr.proto import transport_pb2 as flwr_dot_proto_dot_transport__pb2
|
|
18
18
|
from flwr.proto import node_pb2 as flwr_dot_proto_dot_node__pb2
|
19
19
|
|
20
20
|
|
21
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x66lwr/proto/message.proto\x12\nflwr.proto\x1a\x16\x66lwr/proto/error.proto\x1a\x1b\x66lwr/proto/recorddict.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x15\x66lwr/proto/node.proto\"|\n\x07Message\x12&\n\x08metadata\x18\x01 \x01(\x0b\x32\x14.flwr.proto.Metadata\x12\'\n\x07\x63ontent\x18\x02 \x01(\x0b\x32\x16.flwr.proto.RecordDict\x12 \n\x05\x65rror\x18\x03 \x01(\x0b\x32\x11.flwr.proto.Error\"\xd0\x02\n\x07\x43ontext\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x0f\n\x07node_id\x18\x02 \x01(\x04\x12\x38\n\x0bnode_config\x18\x03 \x03(\x0b\x32#.flwr.proto.Context.NodeConfigEntry\x12%\n\x05state\x18\x04 \x01(\x0b\x32\x16.flwr.proto.RecordDict\x12\x36\n\nrun_config\x18\x05 \x03(\x0b\x32\".flwr.proto.Context.RunConfigEntry\x1a\x45\n\x0fNodeConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\x1a\x44\n\x0eRunConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"\xbe\x01\n\x08Metadata\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x12\n\nmessage_id\x18\x02 \x01(\t\x12\x13\n\x0bsrc_node_id\x18\x03 \x01(\x04\x12\x13\n\x0b\x64st_node_id\x18\x04 \x01(\x04\x12\x1b\n\x13reply_to_message_id\x18\x05 \x01(\t\x12\x10\n\x08group_id\x18\x06 \x01(\t\x12\x0b\n\x03ttl\x18\x07 \x01(\x01\x12\x14\n\x0cmessage_type\x18\x08 \x01(\t\x12\x12\n\ncreated_at\x18\t \x01(\x01\"\x1f\n\tObjectIDs\x12\x12\n\nobject_ids\x18\x01 \x03(\t\"n\n\x11PushObjectRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x0e\n\x06run_id\x18\x02 \x01(\x04\x12\x11\n\tobject_id\x18\x03 \x01(\t\x12\x16\n\x0eobject_content\x18\x04 \x01(\x0c\"$\n\x12PushObjectResponse\x12\x0e\n\x06stored\x18\x01 \x01(\x08\"V\n\x11PullObjectRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x0e\n\x06run_id\x18\x02 \x01(\x04\x12\x11\n\tobject_id\x18\x03 \x01(\t\"
|
21
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18\x66lwr/proto/message.proto\x12\nflwr.proto\x1a\x16\x66lwr/proto/error.proto\x1a\x1b\x66lwr/proto/recorddict.proto\x1a\x1a\x66lwr/proto/transport.proto\x1a\x15\x66lwr/proto/node.proto\"|\n\x07Message\x12&\n\x08metadata\x18\x01 \x01(\x0b\x32\x14.flwr.proto.Metadata\x12\'\n\x07\x63ontent\x18\x02 \x01(\x0b\x32\x16.flwr.proto.RecordDict\x12 \n\x05\x65rror\x18\x03 \x01(\x0b\x32\x11.flwr.proto.Error\"\xd0\x02\n\x07\x43ontext\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x0f\n\x07node_id\x18\x02 \x01(\x04\x12\x38\n\x0bnode_config\x18\x03 \x03(\x0b\x32#.flwr.proto.Context.NodeConfigEntry\x12%\n\x05state\x18\x04 \x01(\x0b\x32\x16.flwr.proto.RecordDict\x12\x36\n\nrun_config\x18\x05 \x03(\x0b\x32\".flwr.proto.Context.RunConfigEntry\x1a\x45\n\x0fNodeConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\x1a\x44\n\x0eRunConfigEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12!\n\x05value\x18\x02 \x01(\x0b\x32\x12.flwr.proto.Scalar:\x02\x38\x01\"\xbe\x01\n\x08Metadata\x12\x0e\n\x06run_id\x18\x01 \x01(\x04\x12\x12\n\nmessage_id\x18\x02 \x01(\t\x12\x13\n\x0bsrc_node_id\x18\x03 \x01(\x04\x12\x13\n\x0b\x64st_node_id\x18\x04 \x01(\x04\x12\x1b\n\x13reply_to_message_id\x18\x05 \x01(\t\x12\x10\n\x08group_id\x18\x06 \x01(\t\x12\x0b\n\x03ttl\x18\x07 \x01(\x01\x12\x14\n\x0cmessage_type\x18\x08 \x01(\t\x12\x12\n\ncreated_at\x18\t \x01(\x01\"\x1f\n\tObjectIDs\x12\x12\n\nobject_ids\x18\x01 \x03(\t\"n\n\x11PushObjectRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x0e\n\x06run_id\x18\x02 \x01(\x04\x12\x11\n\tobject_id\x18\x03 \x01(\t\x12\x16\n\x0eobject_content\x18\x04 \x01(\x0c\"$\n\x12PushObjectResponse\x12\x0e\n\x06stored\x18\x01 \x01(\x08\"V\n\x11PullObjectRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x0e\n\x06run_id\x18\x02 \x01(\x04\x12\x11\n\tobject_id\x18\x03 \x01(\t\"\\\n\x12PullObjectResponse\x12\x14\n\x0cobject_found\x18\x01 \x01(\x08\x12\x18\n\x10object_available\x18\x02 \x01(\x08\x12\x16\n\x0eobject_content\x18\x03 \x01(\x0c\x62\x06proto3')
|
22
22
|
|
23
23
|
_globals = globals()
|
24
24
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
@@ -48,5 +48,5 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
|
48
48
|
_globals['_PULLOBJECTREQUEST']._serialized_start=985
|
49
49
|
_globals['_PULLOBJECTREQUEST']._serialized_end=1071
|
50
50
|
_globals['_PULLOBJECTRESPONSE']._serialized_start=1073
|
51
|
-
_globals['_PULLOBJECTRESPONSE']._serialized_end=
|
51
|
+
_globals['_PULLOBJECTRESPONSE']._serialized_end=1165
|
52
52
|
# @@protoc_insertion_point(module_scope)
|
flwr/proto/message_pb2.pyi
CHANGED
@@ -196,11 +196,17 @@ global___PullObjectRequest = PullObjectRequest
|
|
196
196
|
|
197
197
|
class PullObjectResponse(google.protobuf.message.Message):
|
198
198
|
DESCRIPTOR: google.protobuf.descriptor.Descriptor
|
199
|
+
OBJECT_FOUND_FIELD_NUMBER: builtins.int
|
200
|
+
OBJECT_AVAILABLE_FIELD_NUMBER: builtins.int
|
199
201
|
OBJECT_CONTENT_FIELD_NUMBER: builtins.int
|
202
|
+
object_found: builtins.bool
|
203
|
+
object_available: builtins.bool
|
200
204
|
object_content: builtins.bytes
|
201
205
|
def __init__(self,
|
202
206
|
*,
|
207
|
+
object_found: builtins.bool = ...,
|
208
|
+
object_available: builtins.bool = ...,
|
203
209
|
object_content: builtins.bytes = ...,
|
204
210
|
) -> None: ...
|
205
|
-
def ClearField(self, field_name: typing_extensions.Literal["object_content",b"object_content"]) -> None: ...
|
211
|
+
def ClearField(self, field_name: typing_extensions.Literal["object_available",b"object_available","object_content",b"object_content","object_found",b"object_found"]) -> None: ...
|
206
212
|
global___PullObjectResponse = PullObjectResponse
|
flwr/server/grid/grpc_grid.py
CHANGED
@@ -28,11 +28,15 @@ from flwr.common.constant import (
|
|
28
28
|
SUPERLINK_NODE_ID,
|
29
29
|
)
|
30
30
|
from flwr.common.grpc import create_channel, on_channel_state_change
|
31
|
+
from flwr.common.inflatable_grpc_utils import (
|
32
|
+
pull_object_from_servicer,
|
33
|
+
push_object_to_servicer,
|
34
|
+
)
|
31
35
|
from flwr.common.logger import log, warn_deprecated_feature
|
36
|
+
from flwr.common.message import get_message_to_descendant_id_mapping
|
32
37
|
from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
|
33
|
-
from flwr.common.serde import
|
38
|
+
from flwr.common.serde import message_to_proto, run_from_proto
|
34
39
|
from flwr.common.typing import Run
|
35
|
-
from flwr.proto.message_pb2 import Message as ProtoMessage # pylint: disable=E0611
|
36
40
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
37
41
|
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
38
42
|
from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
|
@@ -198,6 +202,35 @@ class GrpcGrid(Grid):
|
|
198
202
|
)
|
199
203
|
return [node.node_id for node in res.nodes]
|
200
204
|
|
205
|
+
def _try_push_message(self, run_id: int, message: Message) -> str:
|
206
|
+
"""Push one message and its associated objects."""
|
207
|
+
# Compute mapping of message descendants
|
208
|
+
descendants_mapping = get_message_to_descendant_id_mapping(message)
|
209
|
+
|
210
|
+
# Call GrpcServerAppIoStub method
|
211
|
+
res: PushInsMessagesResponse = self._stub.PushMessages(
|
212
|
+
PushInsMessagesRequest(
|
213
|
+
messages_list=[message_to_proto(message)],
|
214
|
+
run_id=run_id,
|
215
|
+
msg_to_descendant_mapping=descendants_mapping,
|
216
|
+
)
|
217
|
+
)
|
218
|
+
|
219
|
+
# Push objects
|
220
|
+
msg_id = res.message_ids[0]
|
221
|
+
# If Message was added to the LinkState correctly
|
222
|
+
if msg_id is not None:
|
223
|
+
obj_ids_to_push = set(res.objects_to_push[msg_id].object_ids)
|
224
|
+
# Push only object that are not in the store
|
225
|
+
push_object_to_servicer(
|
226
|
+
message,
|
227
|
+
self._stub,
|
228
|
+
node=self.node,
|
229
|
+
run_id=run_id,
|
230
|
+
object_ids_to_push=obj_ids_to_push,
|
231
|
+
)
|
232
|
+
return msg_id
|
233
|
+
|
201
234
|
def push_messages(self, messages: Iterable[Message]) -> Iterable[str]:
|
202
235
|
"""Push messages to specified node IDs.
|
203
236
|
|
@@ -206,58 +239,64 @@ class GrpcGrid(Grid):
|
|
206
239
|
"""
|
207
240
|
# Construct Messages
|
208
241
|
run_id = cast(Run, self._run).run_id
|
209
|
-
|
210
|
-
for msg in messages:
|
211
|
-
# Populate metadata
|
212
|
-
msg.metadata.__dict__["_run_id"] = run_id
|
213
|
-
msg.metadata.__dict__["_src_node_id"] = self.node.node_id
|
214
|
-
msg.metadata.__dict__["_message_id"] = msg.object_id
|
215
|
-
# Check message
|
216
|
-
self._check_message(msg)
|
217
|
-
# Convert to proto
|
218
|
-
msg_proto = message_to_proto(msg)
|
219
|
-
# Add to list
|
220
|
-
message_proto_list.append(msg_proto)
|
221
|
-
|
242
|
+
message_ids: list[str] = []
|
222
243
|
try:
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
"list has `None` for those messages (the order is preserved as "
|
234
|
-
"passed to `push_messages`). This could be due to a malformed "
|
235
|
-
"message.",
|
236
|
-
)
|
237
|
-
return list(res.message_ids)
|
244
|
+
for msg in messages:
|
245
|
+
# Populate metadata
|
246
|
+
msg.metadata.__dict__["_run_id"] = run_id
|
247
|
+
msg.metadata.__dict__["_src_node_id"] = self.node.node_id
|
248
|
+
msg.metadata.__dict__["_message_id"] = msg.object_id
|
249
|
+
# Check message
|
250
|
+
self._check_message(msg)
|
251
|
+
# Try pushing message and its objects
|
252
|
+
message_ids.append(self._try_push_message(run_id, msg))
|
253
|
+
|
238
254
|
except grpc.RpcError as e:
|
239
255
|
if e.code() == grpc.StatusCode.RESOURCE_EXHAUSTED: # pylint: disable=E1101
|
240
256
|
log(ERROR, ERROR_MESSAGE_PUSH_MESSAGES_RESOURCE_EXHAUSTED)
|
241
257
|
return []
|
242
258
|
raise
|
243
259
|
|
260
|
+
if None in message_ids:
|
261
|
+
log(
|
262
|
+
WARNING,
|
263
|
+
"Not all messages could be pushed to the SuperLink. The returned "
|
264
|
+
"list has `None` for those messages (the order is preserved as "
|
265
|
+
"passed to `push_messages`). This could be due to a malformed "
|
266
|
+
"message.",
|
267
|
+
)
|
268
|
+
|
269
|
+
return message_ids
|
270
|
+
|
244
271
|
def pull_messages(self, message_ids: Iterable[str]) -> Iterable[Message]:
|
245
272
|
"""Pull messages based on message IDs.
|
246
273
|
|
247
274
|
This method is used to collect messages from the SuperLink that correspond to a
|
248
275
|
set of given message IDs.
|
249
276
|
"""
|
277
|
+
run_id = cast(Run, self._run).run_id
|
250
278
|
try:
|
251
279
|
# Pull Messages
|
252
280
|
res: PullResMessagesResponse = self._stub.PullMessages(
|
253
281
|
PullResMessagesRequest(
|
254
282
|
message_ids=message_ids,
|
255
|
-
run_id=
|
283
|
+
run_id=run_id,
|
256
284
|
)
|
257
285
|
)
|
258
|
-
#
|
259
|
-
|
260
|
-
|
286
|
+
# Pull Messages from store
|
287
|
+
inflated_msgs: list[Message] = []
|
288
|
+
for msg_proto in res.messages_list:
|
289
|
+
|
290
|
+
message = pull_object_from_servicer(
|
291
|
+
msg_proto.metadata.message_id,
|
292
|
+
self._stub,
|
293
|
+
node=self.node,
|
294
|
+
run_id=run_id,
|
295
|
+
)
|
296
|
+
inflated_msgs.append(cast(Message, message))
|
297
|
+
|
298
|
+
return inflated_msgs
|
299
|
+
|
261
300
|
except grpc.RpcError as e:
|
262
301
|
if e.code() == grpc.StatusCode.RESOURCE_EXHAUSTED: # pylint: disable=E1101
|
263
302
|
log(ERROR, ERROR_MESSAGE_PULL_MESSAGES_RESOURCE_EXHAUSTED)
|
@@ -15,11 +15,12 @@
|
|
15
15
|
"""Fleet API gRPC request-response servicer."""
|
16
16
|
|
17
17
|
|
18
|
-
from logging import DEBUG, INFO
|
18
|
+
from logging import DEBUG, ERROR, INFO
|
19
19
|
|
20
20
|
import grpc
|
21
21
|
from google.protobuf.json_format import MessageToDict
|
22
22
|
|
23
|
+
from flwr.common.constant import Status
|
23
24
|
from flwr.common.inflatable import check_body_len_consistency
|
24
25
|
from flwr.common.logger import log
|
25
26
|
from flwr.common.typing import InvalidRunStatusException
|
@@ -49,8 +50,9 @@ from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=
|
|
49
50
|
from flwr.server.superlink.ffs.ffs_factory import FfsFactory
|
50
51
|
from flwr.server.superlink.fleet.message_handler import message_handler
|
51
52
|
from flwr.server.superlink.linkstate import LinkStateFactory
|
52
|
-
from flwr.server.superlink.utils import abort_grpc_context
|
53
|
+
from flwr.server.superlink.utils import abort_grpc_context, check_abort
|
53
54
|
from flwr.supercore.object_store import ObjectStoreFactory
|
55
|
+
from flwr.supercore.object_store.object_store import NoObjectInStoreError
|
54
56
|
|
55
57
|
|
56
58
|
class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
@@ -183,11 +185,39 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
183
185
|
request.object_id,
|
184
186
|
)
|
185
187
|
|
188
|
+
state = self.state_factory.state()
|
189
|
+
|
190
|
+
# Abort if the run is not running
|
191
|
+
abort_msg = check_abort(
|
192
|
+
request.run_id,
|
193
|
+
[Status.PENDING, Status.STARTING, Status.FINISHED],
|
194
|
+
state,
|
195
|
+
)
|
196
|
+
if abort_msg:
|
197
|
+
abort_grpc_context(abort_msg, context)
|
198
|
+
|
199
|
+
if request.node.node_id not in state.get_nodes(run_id=request.run_id):
|
200
|
+
# Cancel insertion in ObjectStore
|
201
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, "Unexpected node ID.")
|
202
|
+
|
186
203
|
if not check_body_len_consistency(request.object_content):
|
187
204
|
# Cancel insertion in ObjectStore
|
188
|
-
context.abort(
|
205
|
+
context.abort(
|
206
|
+
grpc.StatusCode.FAILED_PRECONDITION, "Unexpected object length"
|
207
|
+
)
|
208
|
+
|
209
|
+
# Init store
|
210
|
+
store = self.objectstore_factory.store()
|
211
|
+
|
212
|
+
# Insert in store
|
213
|
+
stored = False
|
214
|
+
try:
|
215
|
+
store.put(request.object_id, request.object_content)
|
216
|
+
stored = True
|
217
|
+
except (NoObjectInStoreError, ValueError) as e:
|
218
|
+
log(ERROR, str(e))
|
189
219
|
|
190
|
-
return PushObjectResponse()
|
220
|
+
return PushObjectResponse(stored=stored)
|
191
221
|
|
192
222
|
def PullObject(
|
193
223
|
self, request: PullObjectRequest, context: grpc.ServicerContext
|
@@ -199,4 +229,31 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
|
|
199
229
|
request.object_id,
|
200
230
|
)
|
201
231
|
|
202
|
-
|
232
|
+
state = self.state_factory.state()
|
233
|
+
|
234
|
+
# Abort if the run is not running
|
235
|
+
abort_msg = check_abort(
|
236
|
+
request.run_id,
|
237
|
+
[Status.PENDING, Status.STARTING, Status.FINISHED],
|
238
|
+
state,
|
239
|
+
)
|
240
|
+
if abort_msg:
|
241
|
+
abort_grpc_context(abort_msg, context)
|
242
|
+
|
243
|
+
if request.node.node_id not in state.get_nodes(run_id=request.run_id):
|
244
|
+
# Cancel insertion in ObjectStore
|
245
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, "Unexpected node ID.")
|
246
|
+
|
247
|
+
# Init store
|
248
|
+
store = self.objectstore_factory.store()
|
249
|
+
|
250
|
+
# Fetch from store
|
251
|
+
content = store.get(request.object_id)
|
252
|
+
if content is not None:
|
253
|
+
object_available = content != b""
|
254
|
+
return PullObjectResponse(
|
255
|
+
object_found=True,
|
256
|
+
object_available=object_available,
|
257
|
+
object_content=content,
|
258
|
+
)
|
259
|
+
return PullObjectResponse(object_found=False, object_available=False)
|
@@ -409,11 +409,39 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
409
409
|
"""Push an object to the ObjectStore."""
|
410
410
|
log(DEBUG, "ServerAppIoServicer.PushObject")
|
411
411
|
|
412
|
+
# Init state
|
413
|
+
state: LinkState = self.state_factory.state()
|
414
|
+
|
415
|
+
# Abort if the run is not running
|
416
|
+
abort_if(
|
417
|
+
request.run_id,
|
418
|
+
[Status.PENDING, Status.STARTING, Status.FINISHED],
|
419
|
+
state,
|
420
|
+
context,
|
421
|
+
)
|
422
|
+
|
423
|
+
if request.node.node_id != SUPERLINK_NODE_ID:
|
424
|
+
# Cancel insertion in ObjectStore
|
425
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, "Unexpected node ID.")
|
426
|
+
|
412
427
|
if not check_body_len_consistency(request.object_content):
|
413
428
|
# Cancel insertion in ObjectStore
|
414
|
-
context.abort(
|
429
|
+
context.abort(
|
430
|
+
grpc.StatusCode.FAILED_PRECONDITION, "Unexpected object length."
|
431
|
+
)
|
432
|
+
|
433
|
+
# Init store
|
434
|
+
store = self.objectstore_factory.store()
|
435
|
+
|
436
|
+
# Insert in store
|
437
|
+
stored = False
|
438
|
+
try:
|
439
|
+
store.put(request.object_id, request.object_content)
|
440
|
+
stored = True
|
441
|
+
except (NoObjectInStoreError, ValueError) as e:
|
442
|
+
log(ERROR, str(e))
|
415
443
|
|
416
|
-
return PushObjectResponse()
|
444
|
+
return PushObjectResponse(stored=stored)
|
417
445
|
|
418
446
|
def PullObject(
|
419
447
|
self, request: PullObjectRequest, context: grpc.ServicerContext
|
@@ -421,7 +449,34 @@ class ServerAppIoServicer(serverappio_pb2_grpc.ServerAppIoServicer):
|
|
421
449
|
"""Pull an object from the ObjectStore."""
|
422
450
|
log(DEBUG, "ServerAppIoServicer.PullObject")
|
423
451
|
|
424
|
-
|
452
|
+
# Init state
|
453
|
+
state: LinkState = self.state_factory.state()
|
454
|
+
|
455
|
+
# Abort if the run is not running
|
456
|
+
abort_if(
|
457
|
+
request.run_id,
|
458
|
+
[Status.PENDING, Status.STARTING, Status.FINISHED],
|
459
|
+
state,
|
460
|
+
context,
|
461
|
+
)
|
462
|
+
|
463
|
+
if request.node.node_id != SUPERLINK_NODE_ID:
|
464
|
+
# Cancel insertion in ObjectStore
|
465
|
+
context.abort(grpc.StatusCode.FAILED_PRECONDITION, "Unexpected node ID.")
|
466
|
+
|
467
|
+
# Init store
|
468
|
+
store = self.objectstore_factory.store()
|
469
|
+
|
470
|
+
# Fetch from store
|
471
|
+
content = store.get(request.object_id)
|
472
|
+
if content is not None:
|
473
|
+
object_available = content != b""
|
474
|
+
return PullObjectResponse(
|
475
|
+
object_found=True,
|
476
|
+
object_available=object_available,
|
477
|
+
object_content=content,
|
478
|
+
)
|
479
|
+
return PullObjectResponse(object_found=False, object_available=False)
|
425
480
|
|
426
481
|
|
427
482
|
def _raise_if(validation_error: bool, request_name: str, detail: str) -> None:
|
@@ -42,8 +42,7 @@ from flwr.common.constant import (
|
|
42
42
|
from flwr.common.exit import ExitCode, flwr_exit
|
43
43
|
from flwr.common.exit_handlers import register_exit_handlers
|
44
44
|
from flwr.common.logger import log
|
45
|
-
|
46
|
-
from ..start_client_internal import start_client_internal
|
45
|
+
from flwr.supernode.start_client_internal import start_client_internal
|
47
46
|
|
48
47
|
|
49
48
|
def flower_supernode() -> None:
|
@@ -48,6 +48,7 @@ def flwr_clientapp() -> None:
|
|
48
48
|
token=args.token,
|
49
49
|
flwr_dir=args.flwr_dir,
|
50
50
|
certificates=None,
|
51
|
+
parent_pid=args.parent_pid,
|
51
52
|
)
|
52
53
|
|
53
54
|
|
@@ -69,5 +70,12 @@ def _parse_args_run_flwr_clientapp() -> argparse.ArgumentParser:
|
|
69
70
|
required=False,
|
70
71
|
help="Unique token generated by SuperNode for each ClientApp execution",
|
71
72
|
)
|
73
|
+
parser.add_argument(
|
74
|
+
"--parent-pid",
|
75
|
+
type=int,
|
76
|
+
default=None,
|
77
|
+
help="The PID of the parent process. When set, the process will terminate "
|
78
|
+
"when the parent process exits.",
|
79
|
+
)
|
72
80
|
add_args_flwr_app_common(parser=parser)
|
73
81
|
return parser
|
@@ -16,6 +16,8 @@
|
|
16
16
|
|
17
17
|
|
18
18
|
import gc
|
19
|
+
import os
|
20
|
+
import threading
|
19
21
|
import time
|
20
22
|
from logging import DEBUG, ERROR, INFO
|
21
23
|
from typing import Optional
|
@@ -54,14 +56,19 @@ from flwr.proto.clientappio_pb2 import (
|
|
54
56
|
from flwr.proto.clientappio_pb2_grpc import ClientAppIoStub
|
55
57
|
|
56
58
|
|
57
|
-
def run_clientapp( # pylint: disable=R0914
|
59
|
+
def run_clientapp( # pylint: disable=R0913, R0914, R0917
|
58
60
|
clientappio_api_address: str,
|
59
61
|
run_once: bool,
|
60
62
|
token: Optional[int] = None,
|
61
63
|
flwr_dir: Optional[str] = None,
|
62
64
|
certificates: Optional[bytes] = None,
|
65
|
+
parent_pid: Optional[int] = None,
|
63
66
|
) -> None:
|
64
67
|
"""Run Flower ClientApp process."""
|
68
|
+
# Monitor the main process in case of SIGKILL
|
69
|
+
if parent_pid is not None:
|
70
|
+
start_parent_process_monitor(parent_pid)
|
71
|
+
|
65
72
|
channel = create_channel(
|
66
73
|
server_address=clientappio_api_address,
|
67
74
|
insecure=(certificates is None),
|
@@ -151,6 +158,20 @@ def run_clientapp( # pylint: disable=R0914
|
|
151
158
|
channel.close()
|
152
159
|
|
153
160
|
|
161
|
+
def start_parent_process_monitor(
|
162
|
+
parent_pid: int,
|
163
|
+
) -> None:
|
164
|
+
"""Monitor the parent process and exit if it terminates."""
|
165
|
+
|
166
|
+
def monitor() -> None:
|
167
|
+
while True:
|
168
|
+
time.sleep(0.2)
|
169
|
+
if os.getppid() != parent_pid:
|
170
|
+
os.kill(os.getpid(), 9)
|
171
|
+
|
172
|
+
threading.Thread(target=monitor, daemon=True).start()
|
173
|
+
|
174
|
+
|
154
175
|
def get_token(stub: grpc.Channel) -> Optional[int]:
|
155
176
|
"""Get a token from SuperNode."""
|
156
177
|
log(DEBUG, "[flwr-clientapp] Request token")
|
@@ -15,29 +15,25 @@
|
|
15
15
|
"""Main loop for Flower SuperNode."""
|
16
16
|
|
17
17
|
|
18
|
-
import multiprocessing
|
19
18
|
import os
|
20
|
-
import
|
21
|
-
import threading
|
19
|
+
import subprocess
|
22
20
|
import time
|
23
21
|
from collections.abc import Iterator
|
24
22
|
from contextlib import contextmanager
|
25
23
|
from logging import INFO, WARN
|
26
24
|
from os import urandom
|
27
25
|
from pathlib import Path
|
28
|
-
from typing import Callable, Optional, Union
|
26
|
+
from typing import Callable, Optional, Union, cast
|
29
27
|
|
30
28
|
import grpc
|
31
29
|
from cryptography.hazmat.primitives.asymmetric import ec
|
32
30
|
from grpc import RpcError
|
33
31
|
|
34
|
-
from flwr.app.error import Error
|
35
|
-
from flwr.cli.config_utils import get_fab_metadata
|
36
32
|
from flwr.client.grpc_adapter_client.connection import grpc_adapter
|
37
33
|
from flwr.client.grpc_rere_client.connection import grpc_request_response
|
38
|
-
from flwr.
|
39
|
-
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Message
|
34
|
+
from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, Message, RecordDict
|
40
35
|
from flwr.common.address import parse_address
|
36
|
+
from flwr.common.config import get_flwr_dir, get_fused_config_from_fab
|
41
37
|
from flwr.common.constant import (
|
42
38
|
CLIENT_OCTET,
|
43
39
|
CLIENTAPPIO_API_DEFAULT_SERVER_ADDRESS,
|
@@ -49,7 +45,6 @@ from flwr.common.constant import (
|
|
49
45
|
TRANSPORT_TYPE_GRPC_RERE,
|
50
46
|
TRANSPORT_TYPE_REST,
|
51
47
|
TRANSPORT_TYPES,
|
52
|
-
ErrorCode,
|
53
48
|
)
|
54
49
|
from flwr.common.exit import ExitCode, flwr_exit
|
55
50
|
from flwr.common.grpc import generic_create_grpc_server
|
@@ -57,10 +52,13 @@ from flwr.common.logger import log
|
|
57
52
|
from flwr.common.retry_invoker import RetryInvoker, RetryState, exponential
|
58
53
|
from flwr.common.typing import Fab, Run, RunNotRunningException, UserConfig
|
59
54
|
from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
|
60
|
-
from flwr.
|
55
|
+
from flwr.server.superlink.ffs import FfsFactory
|
56
|
+
from flwr.supercore.object_store import ObjectStoreFactory
|
61
57
|
from flwr.supernode.nodestate import NodeStateFactory
|
62
58
|
from flwr.supernode.servicer.clientappio import ClientAppInputs, ClientAppIoServicer
|
63
59
|
|
60
|
+
DEFAULT_FFS_DIR = get_flwr_dir() / "supernode" / "ffs"
|
61
|
+
|
64
62
|
|
65
63
|
# pylint: disable=import-outside-toplevel
|
66
64
|
# pylint: disable=too-many-branches
|
@@ -139,13 +137,15 @@ def start_client_internal(
|
|
139
137
|
certificates=None,
|
140
138
|
)
|
141
139
|
|
142
|
-
#
|
143
|
-
run_info_store: Optional[DeprecatedRunInfoStore] = None
|
140
|
+
# Initialize factories
|
144
141
|
state_factory = NodeStateFactory()
|
145
|
-
|
146
|
-
|
142
|
+
ffs_factory = FfsFactory(get_flwr_dir(flwr_path) / "supernode" / "ffs") # type: ignore
|
143
|
+
object_store_factory = ObjectStoreFactory()
|
147
144
|
|
148
|
-
|
145
|
+
# Initialize NodeState, Ffs, and ObjectStore
|
146
|
+
state = state_factory.state()
|
147
|
+
ffs = ffs_factory.ffs()
|
148
|
+
_store = object_store_factory.store()
|
149
149
|
|
150
150
|
with _init_connection(
|
151
151
|
transport=transport,
|
@@ -158,73 +158,79 @@ def start_client_internal(
|
|
158
158
|
) as conn:
|
159
159
|
receive, send, create_node, _, get_run, get_fab = conn
|
160
160
|
|
161
|
-
#
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
raise ValueError("Failed to register SuperNode with the SuperLink")
|
167
|
-
state.set_node_id(node_id)
|
168
|
-
run_info_store = DeprecatedRunInfoStore(
|
169
|
-
node_id=state.get_node_id(),
|
170
|
-
node_config=node_config,
|
171
|
-
)
|
161
|
+
# Call create_node fn to register node
|
162
|
+
# and store node_id in state
|
163
|
+
if (node_id := create_node()) is None:
|
164
|
+
raise ValueError("Failed to register SuperNode with the SuperLink")
|
165
|
+
state.set_node_id(node_id)
|
172
166
|
|
173
167
|
# pylint: disable=too-many-nested-blocks
|
174
168
|
while True:
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
if len(message.metadata.group_id) > 0:
|
184
|
-
log(
|
185
|
-
INFO,
|
186
|
-
"[RUN %s, ROUND %s]",
|
187
|
-
message.metadata.run_id,
|
188
|
-
message.metadata.group_id,
|
189
|
-
)
|
169
|
+
# Pull message
|
170
|
+
if (message := receive()) is None:
|
171
|
+
time.sleep(3)
|
172
|
+
continue
|
173
|
+
|
174
|
+
# Log message reception
|
175
|
+
log(INFO, "")
|
176
|
+
if message.metadata.group_id:
|
190
177
|
log(
|
191
178
|
INFO,
|
192
|
-
"
|
193
|
-
message.metadata.
|
194
|
-
message.metadata.
|
179
|
+
"[RUN %s, ROUND %s]",
|
180
|
+
message.metadata.run_id,
|
181
|
+
message.metadata.group_id,
|
195
182
|
)
|
183
|
+
else:
|
184
|
+
log(INFO, "[RUN %s]", message.metadata.run_id)
|
185
|
+
log(
|
186
|
+
INFO,
|
187
|
+
"Received: %s message %s",
|
188
|
+
message.metadata.message_type,
|
189
|
+
message.metadata.message_id,
|
190
|
+
)
|
196
191
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
fab =
|
208
|
-
|
209
|
-
|
210
|
-
|
192
|
+
# Ensure the run and FAB are available
|
193
|
+
run_id = message.metadata.run_id
|
194
|
+
try:
|
195
|
+
# Check if the message is from an unknown run
|
196
|
+
if (run_info := state.get_run(run_id)) is None:
|
197
|
+
# Pull run info from SuperLink
|
198
|
+
run_info = get_run(run_id)
|
199
|
+
state.store_run(run_info)
|
200
|
+
|
201
|
+
# Pull and store the FAB
|
202
|
+
fab = get_fab(run_info.fab_hash, run_id)
|
203
|
+
ffs.put(fab.content, {})
|
204
|
+
|
205
|
+
# Initialize the context
|
206
|
+
run_cfg = get_fused_config_from_fab(fab.content, run_info)
|
207
|
+
run_ctx = Context(
|
208
|
+
run_id=run_id,
|
209
|
+
node_id=state.get_node_id(),
|
210
|
+
node_config=node_config,
|
211
|
+
state=RecordDict(),
|
212
|
+
run_config=run_cfg,
|
213
|
+
)
|
214
|
+
state.store_context(run_ctx)
|
211
215
|
|
212
|
-
#
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
216
|
+
# Store the message in the state
|
217
|
+
state.store_message(message)
|
218
|
+
except RunNotRunningException:
|
219
|
+
log(
|
220
|
+
INFO,
|
221
|
+
"Run ID %s is not in `RUNNING` status. Ignoring message %s.",
|
222
|
+
run_id,
|
223
|
+
message.metadata.message_id,
|
218
224
|
)
|
225
|
+
time.sleep(3)
|
226
|
+
continue
|
219
227
|
|
220
|
-
|
221
|
-
context
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
reply_to=message,
|
227
|
-
)
|
228
|
+
try:
|
229
|
+
# Retrieve message, context, run and fab for this run
|
230
|
+
message = state.get_messages(run_ids=[run_id], is_reply=False)[0]
|
231
|
+
context = cast(Context, state.get_context(run_id))
|
232
|
+
run = cast(Run, state.get_run(run_id))
|
233
|
+
fab = Fab(run.fab_hash, ffs.get(run.fab_hash)[0]) # type: ignore
|
228
234
|
|
229
235
|
# Two isolation modes:
|
230
236
|
# 1. `subprocess`: SuperNode is starting the ClientApp
|
@@ -265,16 +271,11 @@ def start_client_internal(
|
|
265
271
|
io_address,
|
266
272
|
"--token",
|
267
273
|
str(token),
|
274
|
+
"--parent-pid",
|
275
|
+
str(os.getpid()),
|
276
|
+
"--insecure",
|
268
277
|
]
|
269
|
-
|
270
|
-
|
271
|
-
proc = mp_spawn_context.Process(
|
272
|
-
target=_run_flwr_clientapp,
|
273
|
-
args=(command, os.getpid()),
|
274
|
-
daemon=True,
|
275
|
-
)
|
276
|
-
proc.start()
|
277
|
-
proc.join()
|
278
|
+
subprocess.run(command, check=False)
|
278
279
|
else:
|
279
280
|
# Wait for output to become available
|
280
281
|
while not clientappio_servicer.has_outputs():
|
@@ -284,10 +285,7 @@ def start_client_internal(
|
|
284
285
|
reply_message, context = outputs.message, outputs.context
|
285
286
|
|
286
287
|
# Update node state
|
287
|
-
|
288
|
-
run_id=run_id,
|
289
|
-
context=context,
|
290
|
-
)
|
288
|
+
state.store_context(context)
|
291
289
|
|
292
290
|
# Send
|
293
291
|
send(reply_message)
|
@@ -421,21 +419,6 @@ def _make_fleet_connection_retry_invoker(
|
|
421
419
|
)
|
422
420
|
|
423
421
|
|
424
|
-
def _run_flwr_clientapp(args: list[str], main_pid: int) -> None:
|
425
|
-
# Monitor the main process in case of SIGKILL
|
426
|
-
def main_process_monitor() -> None:
|
427
|
-
while True:
|
428
|
-
time.sleep(1)
|
429
|
-
if os.getppid() != main_pid:
|
430
|
-
os.kill(os.getpid(), 9)
|
431
|
-
|
432
|
-
threading.Thread(target=main_process_monitor, daemon=True).start()
|
433
|
-
|
434
|
-
# Run the command
|
435
|
-
sys.argv = args
|
436
|
-
flwr_clientapp()
|
437
|
-
|
438
|
-
|
439
422
|
def run_clientappio_api_grpc(
|
440
423
|
address: str,
|
441
424
|
certificates: Optional[tuple[bytes, bytes, bytes]],
|
{flwr_nightly-1.19.0.dev20250602.dist-info → flwr_nightly-1.19.0.dev20250604.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.dev20250604
|
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.19.0.dev20250602.dist-info → flwr_nightly-1.19.0.dev20250604.dist-info}/RECORD
RENAMED
@@ -84,7 +84,7 @@ flwr/client/grpc_adapter_client/__init__.py,sha256=RQWP5mFPROLHKgombiRvPXVWSoVrQ
|
|
84
84
|
flwr/client/grpc_adapter_client/connection.py,sha256=aj5tTYyE8z2hQLXPPydsJiz8gBDIWLUhfWvqYkAL1L4,3966
|
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=kjnbUNLNz3fn-79fOQ-EDPIkzK8W0GckYIUkvWCXDD0,13283
|
88
88
|
flwr/client/grpc_rere_client/grpc_adapter.py,sha256=JvMZ7vCFTaTEo6AzKYh3zDmeQAU7VSjdysbC6t3ufWg,6351
|
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
|
@@ -122,8 +122,8 @@ flwr/common/exit/exit_code.py,sha256=PNEnCrZfOILjfDAFu5m-2YWEJBrk97xglq4zCUlqV7E
|
|
122
122
|
flwr/common/exit_handlers.py,sha256=IaqJ60fXZuu7McaRYnoYKtlbH9t4Yl9goNExKqtmQbs,4304
|
123
123
|
flwr/common/grpc.py,sha256=manTaHaPiyYngUq1ErZvvV2B2GxlXUUUGRy3jc3TBIQ,9798
|
124
124
|
flwr/common/heartbeat.py,sha256=SyEpNDnmJ0lni0cWO67rcoJVKasCLmkNHm3dKLeNrLU,5749
|
125
|
-
flwr/common/inflatable.py,sha256=
|
126
|
-
flwr/common/inflatable_grpc_utils.py,sha256=
|
125
|
+
flwr/common/inflatable.py,sha256=9yPsSFOfNM2OIb15JQ6-wY5kblwXiC5zNX-tsp2ZwW0,7017
|
126
|
+
flwr/common/inflatable_grpc_utils.py,sha256=YGP8oJRfnkwvY6segWH1DUf_ljDIkku7-2zH66tv3HA,4337
|
127
127
|
flwr/common/logger.py,sha256=JbRf6E2vQxXzpDBq1T8IDUJo_usu3gjWEBPQ6uKcmdg,13049
|
128
128
|
flwr/common/message.py,sha256=HfSeqxwXgf90ilbMlM0vrF4cJWqJVx3jJ0gNmTfgdFw,19628
|
129
129
|
flwr/common/object_ref.py,sha256=p3SfTeqo3Aj16SkB-vsnNn01zswOPdGNBitcbRnqmUk,9134
|
@@ -134,11 +134,11 @@ flwr/common/record/array.py,sha256=3K01tAf_jedub2r2-vkRshbsjBSiKErAO4KqDgdDaSo,1
|
|
134
134
|
flwr/common/record/arrayrecord.py,sha256=CpoqYXM6Iv4XEc9SryCMYmw-bIvP8ut6xWJzRwYJzdU,18008
|
135
135
|
flwr/common/record/configrecord.py,sha256=G7U0q39kB0Kyi0zMxFmPxcVemL9NgwVS1qjvI4BRQuU,9763
|
136
136
|
flwr/common/record/conversion_utils.py,sha256=wbNCzy7oAqaA3-arhls_EqRZYXRC4YrWIoE-Gy82fJ0,1191
|
137
|
-
flwr/common/record/metricrecord.py,sha256=
|
137
|
+
flwr/common/record/metricrecord.py,sha256=KOyJjJbvFV6IwBPbgm92FZ_0_hXpMHuwfCi1rh5Zddk,8954
|
138
138
|
flwr/common/record/recorddict.py,sha256=p7hBimFpKM1XKUe6OAkR_7pYGzGL_EwUJUvJ8odZEcY,14986
|
139
139
|
flwr/common/record/typeddict.py,sha256=dDKgUThs2BscYUNcgP82KP8-qfAYXYftDrf2LszAC_o,3599
|
140
140
|
flwr/common/recorddict_compat.py,sha256=D5SqXWkqBddn5b6K_5UoH7aZ11UaN3lDTlzvHx3-rqk,14119
|
141
|
-
flwr/common/retry_invoker.py,sha256=
|
141
|
+
flwr/common/retry_invoker.py,sha256=s5IGgRovE19laMetHFePoqIdMBYfz_KdXs-KyfaCrXw,14634
|
142
142
|
flwr/common/secure_aggregation/__init__.py,sha256=MgW6uHGhyFLBAYQqa1Vzs5n2Gc0d4yEw1_NmerFir70,731
|
143
143
|
flwr/common/secure_aggregation/crypto/__init__.py,sha256=5E4q4-Fw0CNz4tLah_QHj7m_rDeM4ucHcFlPWB_Na3Q,738
|
144
144
|
flwr/common/secure_aggregation/crypto/shamir.py,sha256=N8pPa5cEksowNoAqfFm5SP3IuxuVi9GGMa3JOtPniQY,3954
|
@@ -194,8 +194,8 @@ flwr/proto/log_pb2.py,sha256=iKaS3MVn1BS4xHu8uGPFCOi1KWtvVx-H9V4jCUIJghs,1393
|
|
194
194
|
flwr/proto/log_pb2.pyi,sha256=ipuhgo40sAHTcRzCsGI1HwIstr5q0THPNk_cf62YyME,1448
|
195
195
|
flwr/proto/log_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
196
196
|
flwr/proto/log_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
|
197
|
-
flwr/proto/message_pb2.py,sha256=
|
198
|
-
flwr/proto/message_pb2.pyi,sha256=
|
197
|
+
flwr/proto/message_pb2.py,sha256=VoFv02FalR-xegoyqVMC1M_rD02sdWdoAAfFkw00k84,4481
|
198
|
+
flwr/proto/message_pb2.pyi,sha256=FmBgs2PsotAdv-OOCEHp4Dc3e6dSo6-CAB3-pgmva4g,9392
|
199
199
|
flwr/proto/message_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
200
200
|
flwr/proto/message_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
|
201
201
|
flwr/proto/node_pb2.py,sha256=BzZfAWIX7lV62bZr9f7x16lUZcpg-EImxnwxQXgCbYg,1045
|
@@ -236,7 +236,7 @@ flwr/server/criterion.py,sha256=G4e-6B48Pc7d5rmGVUpIzNKb6UF88O3VmTRuUltgjzM,1061
|
|
236
236
|
flwr/server/fleet_event_log_interceptor.py,sha256=AkL7Y5d3xm2vRhL3ahmEVVoOvAP7PA7dRgB-je4v-Ys,3774
|
237
237
|
flwr/server/grid/__init__.py,sha256=aWZHezoR2UGMJISB_gPMCm2N_2GSbm97A3lAp7ruhRQ,888
|
238
238
|
flwr/server/grid/grid.py,sha256=naGCYt5J6dnmUvrcGkdNyKPe3MBd-0awGm1ALmgahqY,6625
|
239
|
-
flwr/server/grid/grpc_grid.py,sha256=
|
239
|
+
flwr/server/grid/grpc_grid.py,sha256=MWESNIUbBp8ownNE1JvWW-xQ7Hb7AyxcorVsOZIkI18,12321
|
240
240
|
flwr/server/grid/inmemory_grid.py,sha256=RjejYT-d-hHuTs1KSs_5wvOdAWKLus8w5_UAcnGt4iw,6168
|
241
241
|
flwr/server/history.py,sha256=cCkFhBN4GoHsYYNk5GG1Y089eKJh2DH_ZJbYPwLaGyk,5026
|
242
242
|
flwr/server/run_serverapp.py,sha256=v0p6jXj2dFxlRUdoEeF1mnaFd9XRQi6dZCflPY6d3qI,2063
|
@@ -271,7 +271,7 @@ flwr/server/strategy/krum.py,sha256=9hjB-5l7lwo7Er2xRauYvNEKAv9KoPCin_TCdYJwQe4,
|
|
271
271
|
flwr/server/strategy/qfedavg.py,sha256=-siSzfuVX8GRkjmyvbj68fnjk02E3EYHl8Ory6v1QzI,10131
|
272
272
|
flwr/server/strategy/strategy.py,sha256=n4r52i5gK4KGToZvcJUeWuEif1tuI0HZUT3YJPTC7UE,7524
|
273
273
|
flwr/server/superlink/__init__.py,sha256=GNSuJ4-N6Z8wun2iZNlXqENt5beUyzC0Gi_tN396bbM,707
|
274
|
-
flwr/server/superlink/ffs/__init__.py,sha256=
|
274
|
+
flwr/server/superlink/ffs/__init__.py,sha256=U3KXwG_SplEvchat27K0LYPoPHzh-cwwT_NHsGlYMt8,908
|
275
275
|
flwr/server/superlink/ffs/disk_ffs.py,sha256=tkJiUa9cIq6Po-9UYMtFpI-GEyY5FMg4RcDKenaky74,3297
|
276
276
|
flwr/server/superlink/ffs/ffs.py,sha256=6w7wy71i7tbuJwqEgdeCa49JejXMEof3jujURN_R7Rg,2395
|
277
277
|
flwr/server/superlink/ffs/ffs_factory.py,sha256=pK-g3LMelvWTV6N9Cd-j-_-FdcGbRFTKNsWaqmlBDSk,1490
|
@@ -284,7 +284,7 @@ flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py,sha256=KouR9PUcrPmMtoLooF4O
|
|
284
284
|
flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py,sha256=iSf0mbBAlig7G6subQwBSVjcUCgSihONKdZ1RmQPTOk,4887
|
285
285
|
flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=OsS-6GgCIzMMZDVu5Y-OKjynHVUrpdc_5OrtuB-IbU0,5174
|
286
286
|
flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=ahDJJ1e-lDxBpeBMgPk7YZt2wB38_QltcpOC0gLbpFs,758
|
287
|
-
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=
|
287
|
+
flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=SsMtj1EeOgumffWtYTQt-ii3JPldszXvP91C3axznq8,9176
|
288
288
|
flwr/server/superlink/fleet/grpc_rere/server_interceptor.py,sha256=DrHubsaLgJCwCeeJPYogQTiP0xYqjxwnT9rh7OP7BoU,6984
|
289
289
|
flwr/server/superlink/fleet/message_handler/__init__.py,sha256=fHsRV0KvJ8HtgSA4_YBsEzuhJLjO8p6xx4aCY2oE1p4,731
|
290
290
|
flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=P43PapLZJKbZ0Oo0kP_KcO5zSMvO53SakQgPMiR5d1M,6500
|
@@ -303,7 +303,7 @@ flwr/server/superlink/linkstate/sqlite_linkstate.py,sha256=sHJPK1w0tP0m2WCXH2F9l
|
|
303
303
|
flwr/server/superlink/linkstate/utils.py,sha256=IeLh7iGRCHU5MEWOl7iriaSE4L__8GWOa2OleXadK5M,15444
|
304
304
|
flwr/server/superlink/serverappio/__init__.py,sha256=Fy4zJuoccZe5mZSEIpOmQvU6YeXFBa1M4eZuXXmJcn8,717
|
305
305
|
flwr/server/superlink/serverappio/serverappio_grpc.py,sha256=6-FUUt0GiLcBPljj8bBrUNeAITUoDQOLzaMihKo52hg,2326
|
306
|
-
flwr/server/superlink/serverappio/serverappio_servicer.py,sha256=
|
306
|
+
flwr/server/superlink/serverappio/serverappio_servicer.py,sha256=qInBXn7xcnNUNIXj_BkjoWfZd96By55gbTsp4onwfDQ,17290
|
307
307
|
flwr/server/superlink/simulation/__init__.py,sha256=Ry8DrNaZCMcQXvUc4FoCN2m3dvUQgWjasfp015o3Ec4,718
|
308
308
|
flwr/server/superlink/simulation/simulationio_grpc.py,sha256=0l0F-UjYEk6W7HZmI28PbJQLFxSi_vBHRkdchgdaSMQ,2224
|
309
309
|
flwr/server/superlink/simulation/simulationio_servicer.py,sha256=aJezU8RSJswcmWm7Eoy0BqsU13jrcfuFwX3ljm-cORM,7719
|
@@ -345,19 +345,19 @@ flwr/superexec/simulation.py,sha256=j6YwUvBN7EQ09ID7MYOCVZ70PGbuyBy8f9bXU0EszEM,
|
|
345
345
|
flwr/superlink/__init__.py,sha256=GNSuJ4-N6Z8wun2iZNlXqENt5beUyzC0Gi_tN396bbM,707
|
346
346
|
flwr/supernode/__init__.py,sha256=KgeCaVvXWrU3rptNR1y0oBp4YtXbAcrnCcJAiOoWkI4,707
|
347
347
|
flwr/supernode/cli/__init__.py,sha256=JuEMr0-s9zv-PEWKuLB9tj1ocNfroSyNJ-oyv7ati9A,887
|
348
|
-
flwr/supernode/cli/flower_supernode.py,sha256=
|
349
|
-
flwr/supernode/cli/flwr_clientapp.py,sha256=
|
348
|
+
flwr/supernode/cli/flower_supernode.py,sha256=ly2AQhbla2sufDaMsENaEALDEd0a4CS4D0eUrUOkHzY,8778
|
349
|
+
flwr/supernode/cli/flwr_clientapp.py,sha256=KfVUO20ZMnUDSGZTJ9I1KkMawFsRV6kdRUmGIRNbg_8,2812
|
350
350
|
flwr/supernode/nodestate/__init__.py,sha256=CyLLObbmmVgfRO88UCM0VMait1dL57mUauUDfuSHsbU,976
|
351
351
|
flwr/supernode/nodestate/in_memory_nodestate.py,sha256=4ZiLA45fMi2bJgmfDNLtiv-gVNru95Bi48xBy7xtatA,5212
|
352
352
|
flwr/supernode/nodestate/nodestate.py,sha256=SgblnKtqzTHRiODwg4QUREw1-uYPQrLzoeTBlROHf_0,4571
|
353
353
|
flwr/supernode/nodestate/nodestate_factory.py,sha256=UYTDCcwK_baHUmkzkJDxL0UEqvtTfOMlQRrROMCd0Xo,1430
|
354
354
|
flwr/supernode/runtime/__init__.py,sha256=JQdqd2EMTn-ORMeTvewYYh52ls0YKP68jrps1qioxu4,718
|
355
|
-
flwr/supernode/runtime/run_clientapp.py,sha256=
|
355
|
+
flwr/supernode/runtime/run_clientapp.py,sha256=cvWSby7u31u97QapWHxJM-Wer6F1k6mbbD-d1gxwxZA,7962
|
356
356
|
flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca8gxdEo,717
|
357
357
|
flwr/supernode/servicer/clientappio/__init__.py,sha256=vJyOjO2FXZ2URbnthmdsgs6948wbYfdq1L1V8Um-Lr8,895
|
358
358
|
flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=LmzkxtNQBn5vVrHc0Bhq2WqaK6-LM2v4kfLBN0PiNNM,8522
|
359
|
-
flwr/supernode/start_client_internal.py,sha256=
|
360
|
-
flwr_nightly-1.19.0.
|
361
|
-
flwr_nightly-1.19.0.
|
362
|
-
flwr_nightly-1.19.0.
|
363
|
-
flwr_nightly-1.19.0.
|
359
|
+
flwr/supernode/start_client_internal.py,sha256=5CwTNV-XmIhwR1jv3G7aQAXGhf6OFWS6U-vmxY1iKGA,16984
|
360
|
+
flwr_nightly-1.19.0.dev20250604.dist-info/METADATA,sha256=xUqCj0YV0Yt1jAVWil6lHAaQkDUMpQqRIFL5tX2yUQo,15910
|
361
|
+
flwr_nightly-1.19.0.dev20250604.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
362
|
+
flwr_nightly-1.19.0.dev20250604.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
|
363
|
+
flwr_nightly-1.19.0.dev20250604.dist-info/RECORD,,
|
{flwr_nightly-1.19.0.dev20250602.dist-info → flwr_nightly-1.19.0.dev20250604.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|