flwr-nightly 1.8.0.dev20240401__py3-none-any.whl → 1.9.0.dev20240403__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

@@ -15,7 +15,7 @@ readme = "README.md"
15
15
  [tool.poetry.dependencies]
16
16
  python = "^3.9"
17
17
  # Mandatory dependencies
18
- flwr-nightly = { version = "1.8.0.dev20240313", extras = ["simulation"] }
18
+ flwr = { version = "^1.8.0", extras = ["simulation"] }
19
19
  flwr-datasets = { version = "0.0.2", extras = ["vision"] }
20
20
  torch = "2.2.1"
21
21
  torchvision = "0.17.1"
@@ -1,4 +1,4 @@
1
- flwr-nightly[simulation]==1.8.0.dev20240313
1
+ flwr[simulation]>=1.8.0
2
2
  flwr-datasets[vision]==0.0.2
3
3
  torch==2.2.1
4
4
  torchvision==0.17.1
flwr/client/app.py CHANGED
@@ -34,6 +34,7 @@ from flwr.common.constant import (
34
34
  TRANSPORT_TYPE_GRPC_RERE,
35
35
  TRANSPORT_TYPE_REST,
36
36
  TRANSPORT_TYPES,
37
+ ErrorCode,
37
38
  )
38
39
  from flwr.common.exit_handlers import register_exit_handlers
39
40
  from flwr.common.logger import log, warn_deprecated_feature
@@ -483,7 +484,7 @@ def _start_client_internal(
483
484
  # Create an error reply message that will never be used to prevent
484
485
  # the used-before-assignment linting error
485
486
  reply_message = message.create_error_reply(
486
- error=Error(code=0, reason="Unknown")
487
+ error=Error(code=ErrorCode.UNKNOWN, reason="Unknown")
487
488
  )
488
489
 
489
490
  # Handle app loading and task message
@@ -491,27 +492,41 @@ def _start_client_internal(
491
492
  # Load ClientApp instance
492
493
  client_app: ClientApp = load_client_app_fn()
493
494
 
495
+ # Execute ClientApp
494
496
  reply_message = client_app(message=message, context=context)
495
- # Update node state
496
- node_state.update_context(
497
- run_id=message.metadata.run_id,
498
- context=context,
499
- )
500
497
  except Exception as ex: # pylint: disable=broad-exception-caught
501
- log(ERROR, "ClientApp raised an exception", exc_info=ex)
502
498
 
503
499
  # Legacy grpc-bidi
504
500
  if transport in ["grpc-bidi", None]:
501
+ log(ERROR, "Client raised an exception.", exc_info=ex)
505
502
  # Raise exception, crash process
506
503
  raise ex
507
504
 
508
505
  # Don't update/change NodeState
509
506
 
510
- # Create error message
507
+ e_code = ErrorCode.CLIENT_APP_RAISED_EXCEPTION
511
508
  # Reason example: "<class 'ZeroDivisionError'>:<'division by zero'>"
512
509
  reason = str(type(ex)) + ":<'" + str(ex) + "'>"
510
+ exc_entity = "ClientApp"
511
+ if isinstance(ex, LoadClientAppError):
512
+ reason = (
513
+ "An exception was raised when attempting to load "
514
+ "`ClientApp`"
515
+ )
516
+ e_code = ErrorCode.LOAD_CLIENT_APP_EXCEPTION
517
+ exc_entity = "SuperNode"
518
+
519
+ log(ERROR, "%s raised an exception", exc_entity, exc_info=ex)
520
+
521
+ # Create error message
513
522
  reply_message = message.create_error_reply(
514
- error=Error(code=0, reason=reason)
523
+ error=Error(code=e_code, reason=reason)
524
+ )
525
+ else:
526
+ # No exception, update node state
527
+ node_state.update_context(
528
+ run_id=message.metadata.run_id,
529
+ context=context,
515
530
  )
516
531
 
517
532
  # Send
flwr/client/client_app.py CHANGED
@@ -28,6 +28,15 @@ from flwr.common.logger import warn_preview_feature
28
28
  from .typing import ClientAppCallable
29
29
 
30
30
 
31
+ class ClientAppException(Exception):
32
+ """Exception raised when an exception is raised while executing a ClientApp."""
33
+
34
+ def __init__(self, message: str):
35
+ ex_name = self.__class__.__name__
36
+ self.message = f"\nException {ex_name} occurred. Message: " + message
37
+ super().__init__(self.message)
38
+
39
+
31
40
  class ClientApp:
32
41
  """Flower ClientApp.
33
42
 
@@ -151,7 +151,7 @@ def grpc_request_response( # pylint: disable=R0914, R0915
151
151
  def create_node() -> None:
152
152
  """Set create_node."""
153
153
  # Call FleetAPI
154
- create_node_request = CreateNodeRequest()
154
+ create_node_request = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
155
155
  create_node_response = retry_invoker.invoke(
156
156
  stub.CreateNode,
157
157
  request=create_node_request,
@@ -201,7 +201,7 @@ def http_request_response( # pylint: disable=R0914, R0915
201
201
 
202
202
  def create_node() -> None:
203
203
  """Set create_node."""
204
- create_node_req_proto = CreateNodeRequest()
204
+ create_node_req_proto = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
205
205
  create_node_req_bytes: bytes = create_node_req_proto.SerializeToString()
206
206
 
207
207
  res = retry_invoker.invoke(
flwr/common/constant.py CHANGED
@@ -41,6 +41,7 @@ PING_DEFAULT_INTERVAL = 30
41
41
  PING_CALL_TIMEOUT = 5
42
42
  PING_BASE_MULTIPLIER = 0.8
43
43
  PING_RANDOM_RANGE = (-0.1, 0.1)
44
+ PING_MAX_INTERVAL = 1e300
44
45
 
45
46
 
46
47
  class MessageType:
@@ -74,3 +75,16 @@ class SType:
74
75
  def __new__(cls) -> SType:
75
76
  """Prevent instantiation."""
76
77
  raise TypeError(f"{cls.__name__} cannot be instantiated.")
78
+
79
+
80
+ class ErrorCode:
81
+ """Error codes for Message's Error."""
82
+
83
+ UNKNOWN = 0
84
+ LOAD_CLIENT_APP_EXCEPTION = 1
85
+ CLIENT_APP_RAISED_EXCEPTION = 2
86
+ NODE_UNAVAILABLE = 3
87
+
88
+ def __new__(cls) -> ErrorCode:
89
+ """Prevent instantiation."""
90
+ raise TypeError(f"{cls.__name__} cannot be instantiated.")
flwr/common/message.py CHANGED
@@ -17,6 +17,7 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  import time
20
+ import warnings
20
21
  from dataclasses import dataclass
21
22
 
22
23
  from .record import RecordSet
@@ -311,6 +312,13 @@ class Message:
311
312
 
312
313
  ttl = msg.meta.ttl - (reply.meta.created_at - msg.meta.created_at)
313
314
  """
315
+ if ttl:
316
+ warnings.warn(
317
+ "A custom TTL was set, but note that the SuperLink does not enforce "
318
+ "the TTL yet. The SuperLink will start enforcing the TTL in a future "
319
+ "version of Flower.",
320
+ stacklevel=2,
321
+ )
314
322
  # If no TTL passed, use default for message creation (will update after
315
323
  # message creation)
316
324
  ttl_ = DEFAULT_TTL if ttl is None else ttl
@@ -349,6 +357,13 @@ class Message:
349
357
  Message
350
358
  A new `Message` instance representing the reply.
351
359
  """
360
+ if ttl:
361
+ warnings.warn(
362
+ "A custom TTL was set, but note that the SuperLink does not enforce "
363
+ "the TTL yet. The SuperLink will start enforcing the TTL in a future "
364
+ "version of Flower.",
365
+ stacklevel=2,
366
+ )
352
367
  # If no TTL passed, use default for message creation (will update after
353
368
  # message creation)
354
369
  ttl_ = DEFAULT_TTL if ttl is None else ttl
flwr/proto/fleet_pb2.py CHANGED
@@ -16,7 +16,7 @@ from flwr.proto import node_pb2 as flwr_dot_proto_dot_node__pb2
16
16
  from flwr.proto import task_pb2 as flwr_dot_proto_dot_task__pb2
17
17
 
18
18
 
19
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x15\x66lwr/proto/task.proto\"\x13\n\x11\x43reateNodeRequest\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\"D\n\x0bPingRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x15\n\rping_interval\x18\x02 \x01(\x01\"\x1f\n\x0cPingResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"F\n\x12PullTaskInsRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"k\n\x13PullTaskInsResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rtask_ins_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.TaskIns\"@\n\x12PushTaskResRequest\x12*\n\rtask_res_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskRes\"\xae\x01\n\x13PushTaskResResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12=\n\x07results\x18\x02 \x03(\x0b\x32,.flwr.proto.PushTaskResResponse.ResultsEntry\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\x86\x03\n\x05\x46leet\x12M\n\nCreateNode\x12\x1d.flwr.proto.CreateNodeRequest\x1a\x1e.flwr.proto.CreateNodeResponse\"\x00\x12M\n\nDeleteNode\x12\x1d.flwr.proto.DeleteNodeRequest\x1a\x1e.flwr.proto.DeleteNodeResponse\"\x00\x12;\n\x04Ping\x12\x17.flwr.proto.PingRequest\x1a\x18.flwr.proto.PingResponse\"\x00\x12P\n\x0bPullTaskIns\x12\x1e.flwr.proto.PullTaskInsRequest\x1a\x1f.flwr.proto.PullTaskInsResponse\"\x00\x12P\n\x0bPushTaskRes\x12\x1e.flwr.proto.PushTaskResRequest\x1a\x1f.flwr.proto.PushTaskResResponse\"\x00\x62\x06proto3')
19
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x15\x66lwr/proto/task.proto\"*\n\x11\x43reateNodeRequest\x12\x15\n\rping_interval\x18\x01 \x01(\x01\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\"D\n\x0bPingRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x15\n\rping_interval\x18\x02 \x01(\x01\"\x1f\n\x0cPingResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\"F\n\x12PullTaskInsRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x10\n\x08task_ids\x18\x02 \x03(\t\"k\n\x13PullTaskInsResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rtask_ins_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.TaskIns\"@\n\x12PushTaskResRequest\x12*\n\rtask_res_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.TaskRes\"\xae\x01\n\x13PushTaskResResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12=\n\x07results\x18\x02 \x03(\x0b\x32,.flwr.proto.PushTaskResResponse.ResultsEntry\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\x86\x03\n\x05\x46leet\x12M\n\nCreateNode\x12\x1d.flwr.proto.CreateNodeRequest\x1a\x1e.flwr.proto.CreateNodeResponse\"\x00\x12M\n\nDeleteNode\x12\x1d.flwr.proto.DeleteNodeRequest\x1a\x1e.flwr.proto.DeleteNodeResponse\"\x00\x12;\n\x04Ping\x12\x17.flwr.proto.PingRequest\x1a\x18.flwr.proto.PingResponse\"\x00\x12P\n\x0bPullTaskIns\x12\x1e.flwr.proto.PullTaskInsRequest\x1a\x1f.flwr.proto.PullTaskInsResponse\"\x00\x12P\n\x0bPushTaskRes\x12\x1e.flwr.proto.PushTaskResRequest\x1a\x1f.flwr.proto.PushTaskResResponse\"\x00\x62\x06proto3')
20
20
 
21
21
  _globals = globals()
22
22
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -26,29 +26,29 @@ if _descriptor._USE_C_DESCRIPTORS == False:
26
26
  _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._options = None
27
27
  _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_options = b'8\001'
28
28
  _globals['_CREATENODEREQUEST']._serialized_start=84
29
- _globals['_CREATENODEREQUEST']._serialized_end=103
30
- _globals['_CREATENODERESPONSE']._serialized_start=105
31
- _globals['_CREATENODERESPONSE']._serialized_end=157
32
- _globals['_DELETENODEREQUEST']._serialized_start=159
33
- _globals['_DELETENODEREQUEST']._serialized_end=210
34
- _globals['_DELETENODERESPONSE']._serialized_start=212
35
- _globals['_DELETENODERESPONSE']._serialized_end=232
36
- _globals['_PINGREQUEST']._serialized_start=234
37
- _globals['_PINGREQUEST']._serialized_end=302
38
- _globals['_PINGRESPONSE']._serialized_start=304
39
- _globals['_PINGRESPONSE']._serialized_end=335
40
- _globals['_PULLTASKINSREQUEST']._serialized_start=337
41
- _globals['_PULLTASKINSREQUEST']._serialized_end=407
42
- _globals['_PULLTASKINSRESPONSE']._serialized_start=409
43
- _globals['_PULLTASKINSRESPONSE']._serialized_end=516
44
- _globals['_PUSHTASKRESREQUEST']._serialized_start=518
45
- _globals['_PUSHTASKRESREQUEST']._serialized_end=582
46
- _globals['_PUSHTASKRESRESPONSE']._serialized_start=585
47
- _globals['_PUSHTASKRESRESPONSE']._serialized_end=759
48
- _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_start=713
49
- _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_end=759
50
- _globals['_RECONNECT']._serialized_start=761
51
- _globals['_RECONNECT']._serialized_end=791
52
- _globals['_FLEET']._serialized_start=794
53
- _globals['_FLEET']._serialized_end=1184
29
+ _globals['_CREATENODEREQUEST']._serialized_end=126
30
+ _globals['_CREATENODERESPONSE']._serialized_start=128
31
+ _globals['_CREATENODERESPONSE']._serialized_end=180
32
+ _globals['_DELETENODEREQUEST']._serialized_start=182
33
+ _globals['_DELETENODEREQUEST']._serialized_end=233
34
+ _globals['_DELETENODERESPONSE']._serialized_start=235
35
+ _globals['_DELETENODERESPONSE']._serialized_end=255
36
+ _globals['_PINGREQUEST']._serialized_start=257
37
+ _globals['_PINGREQUEST']._serialized_end=325
38
+ _globals['_PINGRESPONSE']._serialized_start=327
39
+ _globals['_PINGRESPONSE']._serialized_end=358
40
+ _globals['_PULLTASKINSREQUEST']._serialized_start=360
41
+ _globals['_PULLTASKINSREQUEST']._serialized_end=430
42
+ _globals['_PULLTASKINSRESPONSE']._serialized_start=432
43
+ _globals['_PULLTASKINSRESPONSE']._serialized_end=539
44
+ _globals['_PUSHTASKRESREQUEST']._serialized_start=541
45
+ _globals['_PUSHTASKRESREQUEST']._serialized_end=605
46
+ _globals['_PUSHTASKRESRESPONSE']._serialized_start=608
47
+ _globals['_PUSHTASKRESRESPONSE']._serialized_end=782
48
+ _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_start=736
49
+ _globals['_PUSHTASKRESRESPONSE_RESULTSENTRY']._serialized_end=782
50
+ _globals['_RECONNECT']._serialized_start=784
51
+ _globals['_RECONNECT']._serialized_end=814
52
+ _globals['_FLEET']._serialized_start=817
53
+ _globals['_FLEET']._serialized_end=1207
54
54
  # @@protoc_insertion_point(module_scope)
flwr/proto/fleet_pb2.pyi CHANGED
@@ -16,8 +16,13 @@ DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
16
16
  class CreateNodeRequest(google.protobuf.message.Message):
17
17
  """CreateNode messages"""
18
18
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
19
+ PING_INTERVAL_FIELD_NUMBER: builtins.int
20
+ ping_interval: builtins.float
19
21
  def __init__(self,
22
+ *,
23
+ ping_interval: builtins.float = ...,
20
24
  ) -> None: ...
25
+ def ClearField(self, field_name: typing_extensions.Literal["ping_interval",b"ping_interval"]) -> None: ...
21
26
  global___CreateNodeRequest = CreateNodeRequest
22
27
 
23
28
  class CreateNodeResponse(google.protobuf.message.Message):
@@ -14,8 +14,8 @@
14
14
  # ==============================================================================
15
15
  """Flower driver service client."""
16
16
 
17
-
18
17
  import time
18
+ import warnings
19
19
  from typing import Iterable, List, Optional, Tuple
20
20
 
21
21
  from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
@@ -91,7 +91,7 @@ class Driver:
91
91
  message_type: str,
92
92
  dst_node_id: int,
93
93
  group_id: str,
94
- ttl: float = DEFAULT_TTL,
94
+ ttl: Optional[float] = None,
95
95
  ) -> Message:
96
96
  """Create a new message with specified parameters.
97
97
 
@@ -111,10 +111,11 @@ class Driver:
111
111
  group_id : str
112
112
  The ID of the group to which this message is associated. In some settings,
113
113
  this is used as the FL round.
114
- ttl : float (default: common.DEFAULT_TTL)
114
+ ttl : Optional[float] (default: None)
115
115
  Time-to-live for the round trip of this message, i.e., the time from sending
116
116
  this message to receiving a reply. It specifies in seconds the duration for
117
- which the message and its potential reply are considered valid.
117
+ which the message and its potential reply are considered valid. If unset,
118
+ the default TTL (i.e., `common.DEFAULT_TTL`) will be used.
118
119
 
119
120
  Returns
120
121
  -------
@@ -122,6 +123,15 @@ class Driver:
122
123
  A new `Message` instance with the specified content and metadata.
123
124
  """
124
125
  _, run_id = self._get_grpc_driver_and_run_id()
126
+ if ttl:
127
+ warnings.warn(
128
+ "A custom TTL was set, but note that the SuperLink does not enforce "
129
+ "the TTL yet. The SuperLink will start enforcing the TTL in a future "
130
+ "version of Flower.",
131
+ stacklevel=2,
132
+ )
133
+
134
+ ttl_ = DEFAULT_TTL if ttl is None else ttl
125
135
  metadata = Metadata(
126
136
  run_id=run_id,
127
137
  message_id="", # Will be set by the server
@@ -129,7 +139,7 @@ class Driver:
129
139
  dst_node_id=dst_node_id,
130
140
  reply_to_message="",
131
141
  group_id=group_id,
132
- ttl=ttl,
142
+ ttl=ttl_,
133
143
  message_type=message_type,
134
144
  )
135
145
  return Message(metadata=metadata, content=content)
@@ -43,7 +43,7 @@ def create_node(
43
43
  ) -> CreateNodeResponse:
44
44
  """."""
45
45
  # Create node
46
- node_id = state.create_node()
46
+ node_id = state.create_node(ping_interval=request.ping_interval)
47
47
  return CreateNodeResponse(node=Node(node_id=node_id, anonymous=False))
48
48
 
49
49
 
@@ -22,8 +22,9 @@ import traceback
22
22
  from logging import DEBUG, ERROR, INFO, WARN
23
23
  from typing import Callable, Dict, List, Optional
24
24
 
25
- from flwr.client.client_app import ClientApp, LoadClientAppError
25
+ from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
26
26
  from flwr.client.node_state import NodeState
27
+ from flwr.common.constant import PING_MAX_INTERVAL, ErrorCode
27
28
  from flwr.common.logger import log
28
29
  from flwr.common.message import Error
29
30
  from flwr.common.object_ref import load_app
@@ -43,7 +44,7 @@ def _register_nodes(
43
44
  nodes_mapping: NodeToPartitionMapping = {}
44
45
  state = state_factory.state()
45
46
  for i in range(num_nodes):
46
- node_id = state.create_node()
47
+ node_id = state.create_node(ping_interval=PING_MAX_INTERVAL)
47
48
  nodes_mapping[node_id] = i
48
49
  log(INFO, "Registered %i nodes", len(nodes_mapping))
49
50
  return nodes_mapping
@@ -93,9 +94,18 @@ async def worker(
93
94
  except Exception as ex: # pylint: disable=broad-exception-caught
94
95
  log(ERROR, ex)
95
96
  log(ERROR, traceback.format_exc())
97
+
98
+ if isinstance(ex, ClientAppException):
99
+ e_code = ErrorCode.CLIENT_APP_RAISED_EXCEPTION
100
+ elif isinstance(ex, LoadClientAppError):
101
+ e_code = ErrorCode.LOAD_CLIENT_APP_EXCEPTION
102
+ else:
103
+ e_code = ErrorCode.UNKNOWN
104
+
96
105
  reason = str(type(ex)) + ":<'" + str(ex) + "'>"
97
- error = Error(code=0, reason=reason)
98
- out_mssg = message.create_error_reply(error=error)
106
+ out_mssg = message.create_error_reply(
107
+ error=Error(code=e_code, reason=reason)
108
+ )
99
109
 
100
110
  finally:
101
111
  if out_mssg:
@@ -27,6 +27,8 @@ from flwr.proto.task_pb2 import TaskIns, TaskRes # pylint: disable=E0611
27
27
  from flwr.server.superlink.state.state import State
28
28
  from flwr.server.utils import validate_task_ins_or_res
29
29
 
30
+ from .utils import make_node_unavailable_taskres
31
+
30
32
 
31
33
  class InMemoryState(State):
32
34
  """In-memory State implementation."""
@@ -129,15 +131,32 @@ class InMemoryState(State):
129
131
  with self.lock:
130
132
  # Find TaskRes that were not delivered yet
131
133
  task_res_list: List[TaskRes] = []
134
+ replied_task_ids: Set[UUID] = set()
132
135
  for _, task_res in self.task_res_store.items():
133
- if (
134
- UUID(task_res.task.ancestry[0]) in task_ids
135
- and task_res.task.delivered_at == ""
136
- ):
136
+ reply_to = UUID(task_res.task.ancestry[0])
137
+ if reply_to in task_ids and task_res.task.delivered_at == "":
137
138
  task_res_list.append(task_res)
139
+ replied_task_ids.add(reply_to)
138
140
  if limit and len(task_res_list) == limit:
139
141
  break
140
142
 
143
+ # Check if the node is offline
144
+ for task_id in task_ids - replied_task_ids:
145
+ if limit and len(task_res_list) == limit:
146
+ break
147
+ task_ins = self.task_ins_store.get(task_id)
148
+ if task_ins is None:
149
+ continue
150
+ node_id = task_ins.task.consumer.node_id
151
+ online_until, _ = self.node_ids[node_id]
152
+ # Generate a TaskRes containing an error reply if the node is offline.
153
+ if online_until < time.time():
154
+ err_taskres = make_node_unavailable_taskres(
155
+ ref_taskins=task_ins,
156
+ )
157
+ self.task_res_store[UUID(err_taskres.task_id)] = err_taskres
158
+ task_res_list.append(err_taskres)
159
+
141
160
  # Mark all of them as delivered
142
161
  delivered_at = now().isoformat()
143
162
  for task_res in task_res_list:
@@ -182,16 +201,14 @@ class InMemoryState(State):
182
201
  """
183
202
  return len(self.task_res_store)
184
203
 
185
- def create_node(self) -> int:
204
+ def create_node(self, ping_interval: float) -> int:
186
205
  """Create, store in state, and return `node_id`."""
187
206
  # Sample a random int64 as node_id
188
207
  node_id: int = int.from_bytes(os.urandom(8), "little", signed=True)
189
208
 
190
209
  with self.lock:
191
210
  if node_id not in self.node_ids:
192
- # Default ping interval is 30s
193
- # TODO: change 1e9 to 30s # pylint: disable=W0511
194
- self.node_ids[node_id] = (time.time() + 1e9, 1e9)
211
+ self.node_ids[node_id] = (time.time() + ping_interval, ping_interval)
195
212
  return node_id
196
213
  log(ERROR, "Unexpected node registration failure.")
197
214
  return 0
@@ -30,6 +30,7 @@ from flwr.proto.task_pb2 import Task, TaskIns, TaskRes # pylint: disable=E0611
30
30
  from flwr.server.utils.validator import validate_task_ins_or_res
31
31
 
32
32
  from .state import State
33
+ from .utils import make_node_unavailable_taskres
33
34
 
34
35
  SQL_CREATE_TABLE_NODE = """
35
36
  CREATE TABLE IF NOT EXISTS node(
@@ -344,6 +345,7 @@ class SqliteState(State):
344
345
 
345
346
  return task_id
346
347
 
348
+ # pylint: disable-next=R0914
347
349
  def get_task_res(self, task_ids: Set[UUID], limit: Optional[int]) -> List[TaskRes]:
348
350
  """Get TaskRes for task_ids.
349
351
 
@@ -374,7 +376,7 @@ class SqliteState(State):
374
376
  AND delivered_at = ""
375
377
  """
376
378
 
377
- data: Dict[str, Union[str, int]] = {}
379
+ data: Dict[str, Union[str, float, int]] = {}
378
380
 
379
381
  if limit is not None:
380
382
  query += " LIMIT :limit"
@@ -408,6 +410,54 @@ class SqliteState(State):
408
410
  rows = self.query(query, data)
409
411
 
410
412
  result = [dict_to_task_res(row) for row in rows]
413
+
414
+ # 1. Query: Fetch consumer_node_id of remaining task_ids
415
+ # Assume the ancestry field only contains one element
416
+ data.clear()
417
+ replied_task_ids: Set[UUID] = {UUID(str(row["ancestry"])) for row in rows}
418
+ remaining_task_ids = task_ids - replied_task_ids
419
+ placeholders = ",".join([f":id_{i}" for i in range(len(remaining_task_ids))])
420
+ query = f"""
421
+ SELECT consumer_node_id
422
+ FROM task_ins
423
+ WHERE task_id IN ({placeholders});
424
+ """
425
+ for index, task_id in enumerate(remaining_task_ids):
426
+ data[f"id_{index}"] = str(task_id)
427
+ node_ids = [int(row["consumer_node_id"]) for row in self.query(query, data)]
428
+
429
+ # 2. Query: Select offline nodes
430
+ placeholders = ",".join([f":id_{i}" for i in range(len(node_ids))])
431
+ query = f"""
432
+ SELECT node_id
433
+ FROM node
434
+ WHERE node_id IN ({placeholders})
435
+ AND online_until < :time;
436
+ """
437
+ data = {f"id_{i}": str(node_id) for i, node_id in enumerate(node_ids)}
438
+ data["time"] = time.time()
439
+ offline_node_ids = [int(row["node_id"]) for row in self.query(query, data)]
440
+
441
+ # 3. Query: Select TaskIns for offline nodes
442
+ placeholders = ",".join([f":id_{i}" for i in range(len(offline_node_ids))])
443
+ query = f"""
444
+ SELECT *
445
+ FROM task_ins
446
+ WHERE consumer_node_id IN ({placeholders});
447
+ """
448
+ data = {f"id_{i}": str(node_id) for i, node_id in enumerate(offline_node_ids)}
449
+ task_ins_rows = self.query(query, data)
450
+
451
+ # Make TaskRes containing node unavailabe error
452
+ for row in task_ins_rows:
453
+ if limit and len(result) == limit:
454
+ break
455
+ task_ins = dict_to_task_ins(row)
456
+ err_taskres = make_node_unavailable_taskres(
457
+ ref_taskins=task_ins,
458
+ )
459
+ result.append(err_taskres)
460
+
411
461
  return result
412
462
 
413
463
  def num_task_ins(self) -> int:
@@ -468,7 +518,7 @@ class SqliteState(State):
468
518
 
469
519
  return None
470
520
 
471
- def create_node(self) -> int:
521
+ def create_node(self, ping_interval: float) -> int:
472
522
  """Create, store in state, and return `node_id`."""
473
523
  # Sample a random int64 as node_id
474
524
  node_id: int = int.from_bytes(os.urandom(8), "little", signed=True)
@@ -478,9 +528,7 @@ class SqliteState(State):
478
528
  )
479
529
 
480
530
  try:
481
- # Default ping interval is 30s
482
- # TODO: change 1e9 to 30s # pylint: disable=W0511
483
- self.query(query, (node_id, time.time() + 1e9, 1e9))
531
+ self.query(query, (node_id, time.time() + ping_interval, ping_interval))
484
532
  except sqlite3.IntegrityError:
485
533
  log(ERROR, "Unexpected node registration failure.")
486
534
  return 0
@@ -132,7 +132,7 @@ class State(abc.ABC):
132
132
  """Delete all delivered TaskIns/TaskRes pairs."""
133
133
 
134
134
  @abc.abstractmethod
135
- def create_node(self) -> int:
135
+ def create_node(self, ping_interval: float) -> int:
136
136
  """Create, store in state, and return `node_id`."""
137
137
 
138
138
  @abc.abstractmethod
@@ -0,0 +1,56 @@
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Utility functions for State."""
16
+
17
+
18
+ import time
19
+ from logging import ERROR
20
+ from uuid import uuid4
21
+
22
+ from flwr.common import log
23
+ from flwr.common.constant import ErrorCode
24
+ from flwr.proto.error_pb2 import Error # pylint: disable=E0611
25
+ from flwr.proto.node_pb2 import Node # pylint: disable=E0611
26
+ from flwr.proto.task_pb2 import Task, TaskIns, TaskRes # pylint: disable=E0611
27
+
28
+ NODE_UNAVAILABLE_ERROR_REASON = (
29
+ "Error: Node Unavailable - The destination node is currently unavailable. "
30
+ "It exceeds the time limit specified in its last ping."
31
+ )
32
+
33
+
34
+ def make_node_unavailable_taskres(ref_taskins: TaskIns) -> TaskRes:
35
+ """Generate a TaskRes with a node unavailable error from a TaskIns."""
36
+ current_time = time.time()
37
+ ttl = ref_taskins.task.ttl - (current_time - ref_taskins.task.created_at)
38
+ if ttl < 0:
39
+ log(ERROR, "Creating TaskRes for TaskIns that exceeds its TTL.")
40
+ ttl = 0
41
+ return TaskRes(
42
+ task_id=str(uuid4()),
43
+ group_id=ref_taskins.group_id,
44
+ run_id=ref_taskins.run_id,
45
+ task=Task(
46
+ producer=Node(node_id=ref_taskins.task.consumer.node_id, anonymous=False),
47
+ consumer=Node(node_id=ref_taskins.task.producer.node_id, anonymous=False),
48
+ created_at=current_time,
49
+ ttl=ttl,
50
+ ancestry=[ref_taskins.task_id],
51
+ task_type=ref_taskins.task.task_type,
52
+ error=Error(
53
+ code=ErrorCode.NODE_UNAVAILABLE, reason=NODE_UNAVAILABLE_ERROR_REASON
54
+ ),
55
+ ),
56
+ )
@@ -21,7 +21,7 @@ from logging import INFO
21
21
  from typing import Optional, cast
22
22
 
23
23
  import flwr.common.recordset_compat as compat
24
- from flwr.common import DEFAULT_TTL, ConfigsRecord, Context, GetParametersIns, log
24
+ from flwr.common import ConfigsRecord, Context, GetParametersIns, log
25
25
  from flwr.common.constant import MessageType, MessageTypeLegacy
26
26
 
27
27
  from ..compat.app_utils import start_update_client_manager_thread
@@ -127,7 +127,6 @@ def default_init_params_workflow(driver: Driver, context: Context) -> None:
127
127
  message_type=MessageTypeLegacy.GET_PARAMETERS,
128
128
  dst_node_id=random_client.node_id,
129
129
  group_id="0",
130
- ttl=DEFAULT_TTL,
131
130
  )
132
131
  ]
133
132
  )
@@ -226,7 +225,6 @@ def default_fit_workflow( # pylint: disable=R0914
226
225
  message_type=MessageType.TRAIN,
227
226
  dst_node_id=proxy.node_id,
228
227
  group_id=str(current_round),
229
- ttl=DEFAULT_TTL,
230
228
  )
231
229
  for proxy, fitins in client_instructions
232
230
  ]
@@ -306,7 +304,6 @@ def default_evaluate_workflow(driver: Driver, context: Context) -> None:
306
304
  message_type=MessageType.EVALUATE,
307
305
  dst_node_id=proxy.node_id,
308
306
  group_id=str(current_round),
309
- ttl=DEFAULT_TTL,
310
307
  )
311
308
  for proxy, evalins in client_instructions
312
309
  ]
@@ -22,7 +22,6 @@ from typing import Dict, List, Optional, Set, Tuple, Union, cast
22
22
 
23
23
  import flwr.common.recordset_compat as compat
24
24
  from flwr.common import (
25
- DEFAULT_TTL,
26
25
  ConfigsRecord,
27
26
  Context,
28
27
  FitRes,
@@ -374,7 +373,6 @@ class SecAggPlusWorkflow:
374
373
  message_type=MessageType.TRAIN,
375
374
  dst_node_id=nid,
376
375
  group_id=str(cfg[WorkflowKey.CURRENT_ROUND]),
377
- ttl=DEFAULT_TTL,
378
376
  )
379
377
 
380
378
  log(
@@ -422,7 +420,6 @@ class SecAggPlusWorkflow:
422
420
  message_type=MessageType.TRAIN,
423
421
  dst_node_id=nid,
424
422
  group_id=str(cfg[WorkflowKey.CURRENT_ROUND]),
425
- ttl=DEFAULT_TTL,
426
423
  )
427
424
 
428
425
  # Broadcast public keys to clients and receive secret key shares
@@ -493,7 +490,6 @@ class SecAggPlusWorkflow:
493
490
  message_type=MessageType.TRAIN,
494
491
  dst_node_id=nid,
495
492
  group_id=str(cfg[WorkflowKey.CURRENT_ROUND]),
496
- ttl=DEFAULT_TTL,
497
493
  )
498
494
 
499
495
  log(
@@ -564,7 +560,6 @@ class SecAggPlusWorkflow:
564
560
  message_type=MessageType.TRAIN,
565
561
  dst_node_id=nid,
566
562
  group_id=str(current_round),
567
- ttl=DEFAULT_TTL,
568
563
  )
569
564
 
570
565
  log(
@@ -16,7 +16,6 @@
16
16
 
17
17
  import asyncio
18
18
  import threading
19
- import traceback
20
19
  from abc import ABC
21
20
  from logging import DEBUG, ERROR, WARNING
22
21
  from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union
@@ -25,22 +24,13 @@ import ray
25
24
  from ray import ObjectRef
26
25
  from ray.util.actor_pool import ActorPool
27
26
 
28
- from flwr.client.client_app import ClientApp, LoadClientAppError
27
+ from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
29
28
  from flwr.common import Context, Message
30
29
  from flwr.common.logger import log
31
30
 
32
31
  ClientAppFn = Callable[[], ClientApp]
33
32
 
34
33
 
35
- class ClientException(Exception):
36
- """Raised when client side logic crashes with an exception."""
37
-
38
- def __init__(self, message: str):
39
- div = ">" * 7
40
- self.message = "\n" + div + "A ClientException occurred." + message
41
- super().__init__(self.message)
42
-
43
-
44
34
  class VirtualClientEngineActor(ABC):
45
35
  """Abstract base class for VirtualClientEngine Actors."""
46
36
 
@@ -71,17 +61,7 @@ class VirtualClientEngineActor(ABC):
71
61
  raise load_ex
72
62
 
73
63
  except Exception as ex:
74
- client_trace = traceback.format_exc()
75
- mssg = (
76
- "\n\tSomething went wrong when running your client run."
77
- "\n\tClient "
78
- + cid
79
- + " crashed when the "
80
- + self.__class__.__name__
81
- + " was running its run."
82
- "\n\tException triggered on the client side: " + client_trace,
83
- )
84
- raise ClientException(str(mssg)) from ex
64
+ raise ClientAppException(str(ex)) from ex
85
65
 
86
66
  return cid, out_message, context
87
67
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.8.0.dev20240401
3
+ Version: 1.9.0.dev20240403
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -19,23 +19,23 @@ flwr/cli/new/templates/app/code/server.tensorflow.py.tpl,sha256=d6J5VM681d0j4hj1
19
19
  flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=e3quQBKqC5-ZKbl7AKLvEs89SdzMk-k30rxeDx9I63c,3675
20
20
  flwr/cli/new/templates/app/flower.toml.tpl,sha256=gJ5MZ7zaiaVvIEt5X_kkU-SU2NmeXkAZ9NXJS00-Axw,269
21
21
  flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=5kGEAPrHKHFfzxmKy1AvrHMcp46nZwnQb2z5zPX8XZY,408
22
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=vymP46k08m01xY3DYnwjeNnXMOWL5Vyc6O55-zk1hd4,507
22
+ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=SUmoMyRodzYkxElKDF_rgP-Ftirw4hKUoOE8EcRpQb0,488
23
23
  flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=6odFG__2Wb8CHNUDJRjA0H3bOkZ8Z7iziTNJKX2_SOI,697
24
24
  flwr/cli/new/templates/app/requirements.numpy.txt.tpl,sha256=YfJLjL-ONv0hnw5k4EfHv6uMpaqfLzOhkT_P4aWv5Fo,30
25
- flwr/cli/new/templates/app/requirements.pytorch.txt.tpl,sha256=x6tcHjufWp8tcf_CrWy_qZfpYirviekHzJlQYhiNaMU,106
25
+ flwr/cli/new/templates/app/requirements.pytorch.txt.tpl,sha256=3dvhiFgiOw3HAHCDmyiqvBSTF492wQm75KUKzVCOkUc,86
26
26
  flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl,sha256=MsiO0GbUe35h5giVMaE2YykKMAhtC5ccAc_4EcmJUNs,209
27
27
  flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
28
28
  flwr/cli/run/run.py,sha256=C7Yh-Y0f64PEabb9733jBKIhhOUFpcRmCZJIDtv-NG8,2329
29
29
  flwr/cli/utils.py,sha256=_V2BlFVNNG2naZrq227fZ8o4TxBN_hB-4fQsen9uQoo,2300
30
30
  flwr/client/__init__.py,sha256=futk_IdY_N1h8BTve4Iru51bxm7H1gv58ZPIXWi5XUA,1187
31
- flwr/client/app.py,sha256=3k1qi_OnN17F7Lwz4zmU6O9Ijsvy9UtfCBjy0qZ3EoY,25445
31
+ flwr/client/app.py,sha256=L8TJxRbo8j58Nr99CY3Gk_zDzWLwS0CQp60QbVMPKd4,26114
32
32
  flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
33
- flwr/client/client_app.py,sha256=jqZGliZH7unwJ6y1lQOpGjrMvuOt0P5plVveaPeJBXw,8315
33
+ flwr/client/client_app.py,sha256=-Cs0084tLQUoBCeYZdG2KgU7cjp95_ZJ4MfjoaN4Fzk,8636
34
34
  flwr/client/dpfedavg_numpy_client.py,sha256=9Tnig4iml2J88HBKNahegjXjbfvIQyBtaIQaqjbeqsA,7435
35
35
  flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
36
36
  flwr/client/grpc_client/connection.py,sha256=w3Lble9-eCzNOR7fBUsVedVCK4ui9QPhK7i7Ew_a5Vk,8717
37
37
  flwr/client/grpc_rere_client/__init__.py,sha256=avn6W_vHEM_yZEB1S7hCZgnTbXb6ZujqRP_vAzyXu-0,752
38
- flwr/client/grpc_rere_client/connection.py,sha256=boYRkyesnpdUWQL-DSA660tINCDGxRIPdXT8hc9uHfo,8525
38
+ flwr/client/grpc_rere_client/connection.py,sha256=JaQIQYUJnmZHfqrGBxYZmEtyC-rUdCCaK1HrMcOXEig,8560
39
39
  flwr/client/heartbeat.py,sha256=6Ix2Du9SDlXU_nre48WIDUXDy3AVoZsGKacSq2NqT5c,2377
40
40
  flwr/client/message_handler/__init__.py,sha256=abHvBRJJiiaAMNgeILQbMOa6h8WqMK2BcnvxwQZFpic,719
41
41
  flwr/client/message_handler/message_handler.py,sha256=ml_FlduAJ5pxO31n1tKRrWfQRSxkMgKLbwXXcRsNSos,6553
@@ -52,11 +52,11 @@ flwr/client/node_state.py,sha256=KTTs_l4I0jBM7IsSsbAGjhfL_yZC3QANbzyvyfZBRDM,177
52
52
  flwr/client/node_state_tests.py,sha256=gPwz0zf2iuDSa11jedkur_u3Xm7lokIDG5ALD2MCvSw,2195
53
53
  flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,10283
54
54
  flwr/client/rest_client/__init__.py,sha256=ThwOnkMdzxo_UuyTI47Q7y9oSpuTgNT2OuFvJCfuDiw,735
55
- flwr/client/rest_client/connection.py,sha256=FdK0hQUFBX4jY68wnlRlFJEKADD81fFT8ubhBI0Mm78,14447
55
+ flwr/client/rest_client/connection.py,sha256=rDLQlymPOZYT4cqOaw8sejlMhmlzyqJL-UrZqyWHv8s,14482
56
56
  flwr/client/typing.py,sha256=c9EvjlEjasxn1Wqx6bGl6Xg6vM1gMFfmXht-E2i5J-k,1006
57
57
  flwr/common/__init__.py,sha256=dHOptgKxna78CEQLD5Yu0QIsoSgpIIw5AhIUZCHDWAU,3721
58
58
  flwr/common/address.py,sha256=iTAN9jtmIGMrWFnx9XZQl45ZEtQJVZZLYPRBSNVARGI,1882
59
- flwr/common/constant.py,sha256=brSqgGzA21eDqL8WxYOb-MTYcnsAkT4g0wseyRPE7rI,2084
59
+ flwr/common/constant.py,sha256=GsixlCwohCBlQ6_mTk29HChuyRftgTELTCN06AxrOyc,2424
60
60
  flwr/common/context.py,sha256=ounF-mWPPtXGwtae3sg5EhF58ScviOa3MVqxRpGVu-8,1313
61
61
  flwr/common/date.py,sha256=UWhBZj49yX9LD4BmatS_ZFZu_-kweGh0KQJ1djyWWH4,891
62
62
  flwr/common/differential_privacy.py,sha256=WZWrL7C9XaB9l9NDkLDI5PvM7jwcoTTFu08ZVG8-M5Q,6113
@@ -65,7 +65,7 @@ flwr/common/dp.py,sha256=Hc3lLHihjexbJaD_ft31gdv9XRcwOTgDBwJzICuok3A,2004
65
65
  flwr/common/exit_handlers.py,sha256=2Nt0wLhc17KQQsLPFSRAjjhUiEFfJK6tNozdGiIY4Fs,2812
66
66
  flwr/common/grpc.py,sha256=HimjpTtIY3Vfqtlq3u-CYWjqAl9rSn0uo3A8JjhUmwQ,2273
67
67
  flwr/common/logger.py,sha256=3hfKun9YISWj4i_QhxgZdnaHJc4x-QvFJQJTKHZ2KHs,6096
68
- flwr/common/message.py,sha256=vgFSCOkPbl60iS4-XQJ8-rHL54MvNc2AwNSSxVl6qYY,11773
68
+ flwr/common/message.py,sha256=NvxiWT9YI8GmIt2r3EPVPFFAFQo3xhP09mvnAxjHivQ,12385
69
69
  flwr/common/object_ref.py,sha256=ELoUCAFO-vbjJC41CGpa-WBG2SLYe3ErW-d9YCG3zqA,4961
70
70
  flwr/common/parameter.py,sha256=-bFAUayToYDF50FZGrBC1hQYJCQDtB2bbr3ZuVLMtdE,2095
71
71
  flwr/common/pyproject.py,sha256=EI_ovbCHGmhYrdPx0RSDi5EkFZFof-8m1PA54c0ZTjc,1385
@@ -99,8 +99,8 @@ flwr/proto/error_pb2.py,sha256=LarjKL90LbwkXKlhzNrDssgl4DXcvIPve8NVCXHpsKA,1084
99
99
  flwr/proto/error_pb2.pyi,sha256=ZNH4HhJTU_KfMXlyCeg8FwU-fcUYxTqEmoJPtWtHikc,734
100
100
  flwr/proto/error_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
101
101
  flwr/proto/error_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
102
- flwr/proto/fleet_pb2.py,sha256=_FkDeb0JUCYlC6YyaKC4tZ74j5TS_s-aYZemAukAzdQ,4316
103
- flwr/proto/fleet_pb2.pyi,sha256=T5zOGs6gnEvtgqu4msnLOd35jcRQ6t13QyyPK6OOuf0,7325
102
+ flwr/proto/fleet_pb2.py,sha256=0PSDvjWer5VDh10L7BckF8-WeNYZzXC2BQQF_E0EacQ,4356
103
+ flwr/proto/fleet_pb2.pyi,sha256=45kQ9YINv3VG0nxWSjCN4SppdepjKW8rRBlxKxz7ud4,7571
104
104
  flwr/proto/fleet_pb2_grpc.py,sha256=U2UeEqWQ7VE58C1ngm_yVniiwBuXfnHmzITYPt6BEtA,9042
105
105
  flwr/proto/fleet_pb2_grpc.pyi,sha256=Cd8oZqhK9ORMB7iKyW0NBvCeP8Bg1OZbnn_8GzDxEHU,2491
106
106
  flwr/proto/node_pb2.py,sha256=1zfXEvgGObglIcaVb4SLFmOcHZvA8eHzEtMFM5A6FYY,1081
@@ -131,7 +131,7 @@ flwr/server/compat/driver_client_proxy.py,sha256=QWLl5YJwI6NVADwjQGQJqkLtCfPNT-a
131
131
  flwr/server/compat/legacy_context.py,sha256=D2s7PvQoDnTexuRmf1uG9Von7GUj4Qqyr7qLklSlKAM,1766
132
132
  flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
133
133
  flwr/server/driver/__init__.py,sha256=yYyVX1FcDiDFM6rw0-DSZpuRy0EoWRfG9puwlQUswFA,820
134
- flwr/server/driver/driver.py,sha256=_crOuSbU9P5jKIjIJ4puTOr7-5sKHtSvNhHY995JN7M,9660
134
+ flwr/server/driver/driver.py,sha256=AwAxgYRx-FI6NvI5ukmdGlEmQRyp5GZSElFnDZhelj8,10106
135
135
  flwr/server/driver/grpc_driver.py,sha256=D2n3_Es_DHFgQsq_TjYVEz8RYJJJYoe24E1vozaTFiE,4586
136
136
  flwr/server/history.py,sha256=hDsoBaA4kUa6d1yvDVXuLluBqOBKSm0_fVDtUtYJkmg,5121
137
137
  flwr/server/run_serverapp.py,sha256=3hoXa57T4L1vOWVWPSSdZ_UyRO-uTwUIrhha6TJAXMg,5592
@@ -175,38 +175,39 @@ flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=1QyBX5qcFPjMVlv7Trvn
175
175
  flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=bEJOMWbSlqkw-y5ZHtEXczhoSlAxErcRYffmTMQAV8M,758
176
176
  flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=LC_ntiLZMIZkspwjtQ9_MZ4agzArebO4HIVJ3YOrFx8,3036
177
177
  flwr/server/superlink/fleet/message_handler/__init__.py,sha256=hEY0l61ojH8Iz30_K1btm1HJ6J49iZJSFUsVYqUTw3A,731
178
- flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=jz8l9xZX_ekv_TUv7pdYsByUWbvnLj1PfsOdct1gZ38,3238
178
+ flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=rVisujx0B0WZROlp4uwk1KjzgPR7Pit4rBnurF5xXUw,3273
179
179
  flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=VKDvDq5H8koOUztpmQacVzGJXPLEEkL1Vmolxt3mvnY,735
180
180
  flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=_tGtARm4x957Fu1EWoDieqOzV9CQZTM4GgKe2GxIOvw,6734
181
181
  flwr/server/superlink/fleet/vce/__init__.py,sha256=36MHKiefnJeyjwMQzVUK4m06Ojon3WDcwZGQsAcyVhQ,783
182
182
  flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=oBIzmnrSSRvH_H0vRGEGWhWzQQwqe3zn6e13RsNwlIY,1466
183
183
  flwr/server/superlink/fleet/vce/backend/backend.py,sha256=LJsKl7oixVvptcG98Rd9ejJycNWcEVB0ODvSreLGp-A,2260
184
184
  flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=TaT2EpbVEsIY0EDzF8obadyZaSXjD38TFGdDPI-ytD0,6375
185
- flwr/server/superlink/fleet/vce/vce_api.py,sha256=8zfJf2_BtvxZHI0-Konpa6sTg9l4KXOLgCIkt38C6CE,12041
185
+ flwr/server/superlink/fleet/vce/vce_api.py,sha256=c2J2m6v1jDyuAhiBArdZNIk4cbiZNFJkpKlBJFEQq-c,12454
186
186
  flwr/server/superlink/state/__init__.py,sha256=ij-7Ms-hyordQdRmGQxY1-nVa4OhixJ0jr7_YDkys0s,1003
187
- flwr/server/superlink/state/in_memory_state.py,sha256=XfdCGRzFut9xlf7AsDAhhAmBw-nKDBjmPWAI--espj0,8707
188
- flwr/server/superlink/state/sqlite_state.py,sha256=1SR6Zz6ud0tSSx940gTfa0vct_GH2n0cX_vnhoAEMlQ,22005
189
- flwr/server/superlink/state/state.py,sha256=mvyih06oUxe5QRUfaSmUuyIYLdP_tkoemNEq19Xt5G0,6036
187
+ flwr/server/superlink/state/in_memory_state.py,sha256=lZPoAwyZE0LcKgef8rFa5dzekIhs2q_TPGv7iddJNKI,9586
188
+ flwr/server/superlink/state/sqlite_state.py,sha256=z2jF0UV0VMsVyVIpT_7v13ji6wuFJSmyNbYZhxwelbE,23985
189
+ flwr/server/superlink/state/state.py,sha256=1cboTXmRGu3r4ebdNby-Ht3qVwAfLgc563YF6awvPSw,6058
190
190
  flwr/server/superlink/state/state_factory.py,sha256=91cSB-KOAFM37z7T098WxTkVeKNaAZ_mTI75snn2_tk,1654
191
+ flwr/server/superlink/state/utils.py,sha256=qhIjBu5_rqm9GLMB6QS5TIRrMDVs85lmY17BqZ1ccLk,2207
191
192
  flwr/server/typing.py,sha256=2zSG-KuDAgwFPuzgVjTLDaEqJ8gXXGqFR2RD-qIk730,913
192
193
  flwr/server/utils/__init__.py,sha256=RQVbo-bcsVtp_lJBf7dL5w01FbLrr7v3YedeGp5_YMs,908
193
194
  flwr/server/utils/tensorboard.py,sha256=k0G6bqsLx7wfYbH2KtXsDYcOCfyIeE12-hefXA7lZdg,5485
194
195
  flwr/server/utils/validator.py,sha256=pzyXoOEEPSoYC2UEzened8IKSFRI-kIqqI0QlwRK9jk,5301
195
196
  flwr/server/workflow/__init__.py,sha256=SXY0XkwbkezFBxxrFB5hKUtmtAgnYISBkPouR1V71ss,902
196
197
  flwr/server/workflow/constant.py,sha256=q4DLdR8Krlxuewq2AQjwTL75hphxE5ODNz4AhViHMXk,1082
197
- flwr/server/workflow/default_workflows.py,sha256=_zAS8yeT4UW2IY9TyWPx9w_C6fUHL79mO980S3UZITs,12655
198
+ flwr/server/workflow/default_workflows.py,sha256=ROJNsY538jSGMaNyF7GHwXMtV7us1Vx8OKyUHWYeDcA,12547
198
199
  flwr/server/workflow/secure_aggregation/__init__.py,sha256=3XlgDOjD_hcukTGl6Bc1B-8M_dPlVSJuTbvXIbiO-Ic,880
199
200
  flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=wpAkYPId0nfK6SgpUAtsCni4_MQLd-uqJ81tUKu3xlI,5838
200
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=3TjJdhYA4xyR7e-cyVBdcGe9os7ErHHayoKBJaH8KcE,29187
201
+ flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=BRqhlnVe8CYNoUvb_KCfRXay02NTT6a-pCrMaOqAxGc,29038
201
202
  flwr/simulation/__init__.py,sha256=hpoKzdovrH0_Cf8HIcXxQxyUUb3BiSk-WUNLf5STHcc,1400
202
203
  flwr/simulation/app.py,sha256=WqJxdXTEuehwMW605p5NMmvBbKYx5tuqnV3Mp7jSWXM,13904
203
204
  flwr/simulation/ray_transport/__init__.py,sha256=FsaAnzC4cw4DqoouBCix6496k29jACkfeIam55BvW9g,734
204
- flwr/simulation/ray_transport/ray_actor.py,sha256=OWjgYW--fswkEDqTP9L_cZblBeUVL59vNz5gvzPAHFk,20054
205
+ flwr/simulation/ray_transport/ray_actor.py,sha256=_wv2eP7qxkCZ-6rMyYWnjLrGPBZRxjvTPjaVk8zIaQ4,19367
205
206
  flwr/simulation/ray_transport/ray_client_proxy.py,sha256=oDu4sEPIOu39vrNi-fqDAe10xtNUXMO49bM2RWfRcyw,6738
206
207
  flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
207
208
  flwr/simulation/run_simulation.py,sha256=HiIH6aa_v56NfKQN5ZBd94NyVfaZNyFs43_kItYsQXU,15685
208
- flwr_nightly-1.8.0.dev20240401.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
209
- flwr_nightly-1.8.0.dev20240401.dist-info/METADATA,sha256=9EApt2x0ajI0-A4ms8brLZ45cWsrgTPSmrKs6X1maPs,15257
210
- flwr_nightly-1.8.0.dev20240401.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
211
- flwr_nightly-1.8.0.dev20240401.dist-info/entry_points.txt,sha256=utu2wybGyYJSTtsB2ktY_gmy-XtMFo9EFZdishX0zR4,320
212
- flwr_nightly-1.8.0.dev20240401.dist-info/RECORD,,
209
+ flwr_nightly-1.9.0.dev20240403.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
210
+ flwr_nightly-1.9.0.dev20240403.dist-info/METADATA,sha256=EInfcIsQaaOnqtDIPwen-CdLHM_ElURkwIxwA8MD5BQ,15257
211
+ flwr_nightly-1.9.0.dev20240403.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
212
+ flwr_nightly-1.9.0.dev20240403.dist-info/entry_points.txt,sha256=utu2wybGyYJSTtsB2ktY_gmy-XtMFo9EFZdishX0zR4,320
213
+ flwr_nightly-1.9.0.dev20240403.dist-info/RECORD,,