flwr-nightly 1.10.0.dev20240711__py3-none-any.whl → 1.10.0.dev20240713__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.

Files changed (36) hide show
  1. flwr/cli/new/templates/app/code/client.hf.py.tpl +6 -2
  2. flwr/cli/new/templates/app/code/client.jax.py.tpl +2 -1
  3. flwr/cli/new/templates/app/code/client.mlx.py.tpl +5 -2
  4. flwr/cli/new/templates/app/code/client.numpy.py.tpl +2 -1
  5. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +5 -2
  6. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +4 -2
  7. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +5 -2
  8. flwr/cli/new/templates/app/code/task.hf.py.tpl +2 -2
  9. flwr/cli/new/templates/app/code/task.mlx.py.tpl +2 -2
  10. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +1 -1
  11. flwr/client/app.py +39 -16
  12. flwr/client/client_app.py +28 -8
  13. flwr/client/grpc_adapter_client/connection.py +1 -1
  14. flwr/client/grpc_client/connection.py +1 -1
  15. flwr/client/grpc_rere_client/connection.py +3 -2
  16. flwr/client/message_handler/message_handler.py +1 -1
  17. flwr/client/node_state.py +10 -5
  18. flwr/client/node_state_tests.py +1 -1
  19. flwr/client/rest_client/connection.py +4 -3
  20. flwr/client/supernode/app.py +13 -7
  21. flwr/client/typing.py +2 -2
  22. flwr/common/config.py +3 -3
  23. flwr/common/constant.py +3 -0
  24. flwr/common/context.py +13 -9
  25. flwr/server/compat/legacy_context.py +1 -1
  26. flwr/server/run_serverapp.py +3 -1
  27. flwr/server/superlink/fleet/vce/backend/raybackend.py +2 -1
  28. flwr/server/superlink/fleet/vce/vce_api.py +16 -3
  29. flwr/simulation/app.py +4 -3
  30. flwr/simulation/ray_transport/ray_client_proxy.py +22 -9
  31. flwr/superexec/app.py +3 -3
  32. {flwr_nightly-1.10.0.dev20240711.dist-info → flwr_nightly-1.10.0.dev20240713.dist-info}/METADATA +1 -1
  33. {flwr_nightly-1.10.0.dev20240711.dist-info → flwr_nightly-1.10.0.dev20240713.dist-info}/RECORD +36 -36
  34. {flwr_nightly-1.10.0.dev20240711.dist-info → flwr_nightly-1.10.0.dev20240713.dist-info}/LICENSE +0 -0
  35. {flwr_nightly-1.10.0.dev20240711.dist-info → flwr_nightly-1.10.0.dev20240713.dist-info}/WHEEL +0 -0
  36. {flwr_nightly-1.10.0.dev20240711.dist-info → flwr_nightly-1.10.0.dev20240713.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  """$project_name: A Flower / HuggingFace Transformers app."""
2
2
 
3
3
  from flwr.client import ClientApp, NumPyClient
4
+ from flwr.common import Context
4
5
  from transformers import AutoModelForSequenceClassification
5
6
 
6
7
  from $import_name.task import (
@@ -38,12 +39,15 @@ class FlowerClient(NumPyClient):
38
39
  return float(loss), len(self.testloader), {"accuracy": accuracy}
39
40
 
40
41
 
41
- def client_fn(cid):
42
+ def client_fn(context: Context):
42
43
  # Load model and data
43
44
  net = AutoModelForSequenceClassification.from_pretrained(
44
45
  CHECKPOINT, num_labels=2
45
46
  ).to(DEVICE)
46
- trainloader, valloader = load_data(int(cid), 2)
47
+
48
+ partition_id = int(context.node_config['partition-id'])
49
+ num_partitions = int(context.node_config['num-partitions])
50
+ trainloader, valloader = load_data(partition_id, num_partitions)
47
51
 
48
52
  # Return Client instance
49
53
  return FlowerClient(net, trainloader, valloader).to_client()
@@ -2,6 +2,7 @@
2
2
 
3
3
  import jax
4
4
  from flwr.client import NumPyClient, ClientApp
5
+ from flwr.common import Context
5
6
 
6
7
  from $import_name.task import (
7
8
  evaluation,
@@ -44,7 +45,7 @@ class FlowerClient(NumPyClient):
44
45
  )
45
46
  return float(loss), num_examples, {"loss": float(loss)}
46
47
 
47
- def client_fn(cid):
48
+ def client_fn(context: Context):
48
49
  # Return Client instance
49
50
  return FlowerClient().to_client()
50
51
 
@@ -4,6 +4,7 @@ import mlx.core as mx
4
4
  import mlx.nn as nn
5
5
  import mlx.optimizers as optim
6
6
  from flwr.client import NumPyClient, ClientApp
7
+ from flwr.common import Context
7
8
 
8
9
  from $import_name.task import (
9
10
  batch_iterate,
@@ -57,8 +58,10 @@ class FlowerClient(NumPyClient):
57
58
  return loss.item(), len(self.test_images), {"accuracy": accuracy.item()}
58
59
 
59
60
 
60
- def client_fn(cid):
61
- data = load_data(int(cid), 2)
61
+ def client_fn(context: Context):
62
+ partition_id = int(context.node_config["partition-id"])
63
+ num_partitions = int(context.node_config["num-partitions"])
64
+ data = load_data(partition_id, num_partitions)
62
65
 
63
66
  # Return Client instance
64
67
  return FlowerClient(data).to_client()
@@ -1,6 +1,7 @@
1
1
  """$project_name: A Flower / NumPy app."""
2
2
 
3
3
  from flwr.client import NumPyClient, ClientApp
4
+ from flwr.common import Context
4
5
  import numpy as np
5
6
 
6
7
 
@@ -15,7 +16,7 @@ class FlowerClient(NumPyClient):
15
16
  return float(0.0), 1, {"accuracy": float(1.0)}
16
17
 
17
18
 
18
- def client_fn(cid: str):
19
+ def client_fn(context: Context):
19
20
  return FlowerClient().to_client()
20
21
 
21
22
 
@@ -1,6 +1,7 @@
1
1
  """$project_name: A Flower / PyTorch app."""
2
2
 
3
3
  from flwr.client import NumPyClient, ClientApp
4
+ from flwr.common import Context
4
5
 
5
6
  from $import_name.task import (
6
7
  Net,
@@ -31,10 +32,12 @@ class FlowerClient(NumPyClient):
31
32
  return loss, len(self.valloader.dataset), {"accuracy": accuracy}
32
33
 
33
34
 
34
- def client_fn(cid):
35
+ def client_fn(context: Context):
35
36
  # Load model and data
36
37
  net = Net().to(DEVICE)
37
- trainloader, valloader = load_data(int(cid), 2)
38
+ partition_id = int(context.node_config["partition-id"])
39
+ num_partitions = int(context.node_config["num-partitions"])
40
+ trainloader, valloader = load_data(partition_id, num_partitions)
38
41
 
39
42
  # Return Client instance
40
43
  return FlowerClient(net, trainloader, valloader).to_client()
@@ -4,6 +4,7 @@ import warnings
4
4
 
5
5
  import numpy as np
6
6
  from flwr.client import NumPyClient, ClientApp
7
+ from flwr.common import Context
7
8
  from flwr_datasets import FederatedDataset
8
9
  from sklearn.linear_model import LogisticRegression
9
10
  from sklearn.metrics import log_loss
@@ -68,8 +69,9 @@ class FlowerClient(NumPyClient):
68
69
 
69
70
  fds = FederatedDataset(dataset="mnist", partitioners={"train": 2})
70
71
 
71
- def client_fn(cid: str):
72
- dataset = fds.load_partition(int(cid), "train").with_format("numpy")
72
+ def client_fn(context: Context):
73
+ partition_id = int(context.node_config["partition-id"])
74
+ dataset = fds.load_partition(partition_id, "train").with_format("numpy")
73
75
 
74
76
  X, y = dataset["image"].reshape((len(dataset), -1)), dataset["label"]
75
77
 
@@ -1,6 +1,7 @@
1
1
  """$project_name: A Flower / TensorFlow app."""
2
2
 
3
3
  from flwr.client import NumPyClient, ClientApp
4
+ from flwr.common import Context
4
5
 
5
6
  from $import_name.task import load_data, load_model
6
7
 
@@ -28,10 +29,12 @@ class FlowerClient(NumPyClient):
28
29
  return loss, len(self.x_test), {"accuracy": accuracy}
29
30
 
30
31
 
31
- def client_fn(cid):
32
+ def client_fn(context: Context):
32
33
  # Load model and data
33
34
  net = load_model()
34
- x_train, y_train, x_test, y_test = load_data(int(cid), 2)
35
+
36
+ partition_id = int(context.node_config["partition-id"])
37
+ x_train, y_train, x_test, y_test = load_data(partition_id, 2)
35
38
 
36
39
  # Return Client instance
37
40
  return FlowerClient(net, x_train, y_train, x_test, y_test).to_client()
@@ -16,9 +16,9 @@ DEVICE = torch.device("cpu")
16
16
  CHECKPOINT = "distilbert-base-uncased" # transformer model checkpoint
17
17
 
18
18
 
19
- def load_data(partition_id, num_clients):
19
+ def load_data(partition_id: int, num_partitions: int):
20
20
  """Load IMDB data (training and eval)"""
21
- fds = FederatedDataset(dataset="imdb", partitioners={"train": num_clients})
21
+ fds = FederatedDataset(dataset="imdb", partitioners={"train": num_partitions})
22
22
  partition = fds.load_partition(partition_id)
23
23
  # Divide data: 80% train, 20% test
24
24
  partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
@@ -43,8 +43,8 @@ def batch_iterate(batch_size, X, y):
43
43
  yield X[ids], y[ids]
44
44
 
45
45
 
46
- def load_data(partition_id, num_clients):
47
- fds = FederatedDataset(dataset="mnist", partitioners={"train": num_clients})
46
+ def load_data(partition_id: int, num_partitions: int):
47
+ fds = FederatedDataset(dataset="mnist", partitioners={"train": num_partitions})
48
48
  partition = fds.load_partition(partition_id)
49
49
  partition_splits = partition.train_test_split(test_size=0.2, seed=42)
50
50
 
@@ -34,7 +34,7 @@ class Net(nn.Module):
34
34
  return self.fc3(x)
35
35
 
36
36
 
37
- def load_data(partition_id, num_partitions):
37
+ def load_data(partition_id: int, num_partitions: int):
38
38
  """Load partition CIFAR10 data."""
39
39
  fds = FederatedDataset(dataset="cifar10", partitioners={"train": num_partitions})
40
40
  partition = fds.load_partition(partition_id)
flwr/client/app.py CHANGED
@@ -18,7 +18,7 @@ import signal
18
18
  import sys
19
19
  import time
20
20
  from dataclasses import dataclass
21
- from logging import DEBUG, ERROR, INFO, WARN
21
+ from logging import ERROR, INFO, WARN
22
22
  from pathlib import Path
23
23
  from typing import Callable, ContextManager, Dict, Optional, Tuple, Type, Union
24
24
 
@@ -28,7 +28,7 @@ from grpc import RpcError
28
28
  from flwr.client.client import Client
29
29
  from flwr.client.client_app import ClientApp, LoadClientAppError
30
30
  from flwr.client.typing import ClientFnExt
31
- from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, Message, event
31
+ from flwr.common import GRPC_MAX_MESSAGE_LENGTH, Context, EventType, Message, event
32
32
  from flwr.common.address import parse_address
33
33
  from flwr.common.constant import (
34
34
  MISSING_EXTRA_REST,
@@ -138,8 +138,8 @@ def start_client(
138
138
 
139
139
  Starting an SSL-enabled gRPC client using system certificates:
140
140
 
141
- >>> def client_fn(node_id: int, partition_id: Optional[int]):
142
- >>> return FlowerClient()
141
+ >>> def client_fn(context: Context):
142
+ >>> return FlowerClient().to_client()
143
143
  >>>
144
144
  >>> start_client(
145
145
  >>> server_address=localhost:8080,
@@ -160,6 +160,7 @@ def start_client(
160
160
  event(EventType.START_CLIENT_ENTER)
161
161
  _start_client_internal(
162
162
  server_address=server_address,
163
+ node_config={},
163
164
  load_client_app_fn=None,
164
165
  client_fn=client_fn,
165
166
  client=client,
@@ -181,6 +182,7 @@ def start_client(
181
182
  def _start_client_internal(
182
183
  *,
183
184
  server_address: str,
185
+ node_config: Dict[str, str],
184
186
  load_client_app_fn: Optional[Callable[[str, str], ClientApp]] = None,
185
187
  client_fn: Optional[ClientFnExt] = None,
186
188
  client: Optional[Client] = None,
@@ -193,7 +195,6 @@ def _start_client_internal(
193
195
  ] = None,
194
196
  max_retries: Optional[int] = None,
195
197
  max_wait_time: Optional[float] = None,
196
- partition_id: Optional[int] = None,
197
198
  flwr_dir: Optional[Path] = None,
198
199
  ) -> None:
199
200
  """Start a Flower client node which connects to a Flower server.
@@ -204,6 +205,8 @@ def _start_client_internal(
204
205
  The IPv4 or IPv6 address of the server. If the Flower
205
206
  server runs on the same machine on port 8080, then `server_address`
206
207
  would be `"[::]:8080"`.
208
+ node_config: Dict[str, str]
209
+ The configuration of the node.
207
210
  load_client_app_fn : Optional[Callable[[], ClientApp]] (default: None)
208
211
  A function that can be used to load a `ClientApp` instance.
209
212
  client_fn : Optional[ClientFnExt]
@@ -238,9 +241,6 @@ def _start_client_internal(
238
241
  The maximum duration before the client stops trying to
239
242
  connect to the server in case of connection error.
240
243
  If set to None, there is no limit to the total time.
241
- partition_id: Optional[int] (default: None)
242
- The data partition index associated with this node. Better suited for
243
- prototyping purposes.
244
244
  flwr_dir: Optional[Path] (default: None)
245
245
  The fully resolved path containing installed Flower Apps.
246
246
  """
@@ -253,8 +253,7 @@ def _start_client_internal(
253
253
  if client_fn is None:
254
254
  # Wrap `Client` instance in `client_fn`
255
255
  def single_client_factory(
256
- node_id: int, # pylint: disable=unused-argument
257
- partition_id: Optional[int], # pylint: disable=unused-argument
256
+ context: Context, # pylint: disable=unused-argument
258
257
  ) -> Client:
259
258
  if client is None: # Added this to keep mypy happy
260
259
  raise ValueError(
@@ -295,7 +294,7 @@ def _start_client_internal(
295
294
  log(WARN, "Connection attempt failed, retrying...")
296
295
  else:
297
296
  log(
298
- DEBUG,
297
+ WARN,
299
298
  "Connection attempt failed, retrying in %.2f seconds",
300
299
  retry_state.actual_wait,
301
300
  )
@@ -319,7 +318,9 @@ def _start_client_internal(
319
318
  on_backoff=_on_backoff,
320
319
  )
321
320
 
322
- node_state = NodeState(partition_id=partition_id)
321
+ # NodeState gets initialized when the first connection is established
322
+ node_state: Optional[NodeState] = None
323
+
323
324
  runs: Dict[int, Run] = {}
324
325
 
325
326
  while not app_state_tracker.interrupt:
@@ -334,9 +335,31 @@ def _start_client_internal(
334
335
  ) as conn:
335
336
  receive, send, create_node, delete_node, get_run = conn
336
337
 
337
- # Register node
338
- if create_node is not None:
339
- create_node() # pylint: disable=not-callable
338
+ # Register node when connecting the first time
339
+ if node_state is None:
340
+ if create_node is None:
341
+ if transport not in ["grpc-bidi", None]:
342
+ raise NotImplementedError(
343
+ "All transports except `grpc-bidi` require "
344
+ "an implementation for `create_node()`.'"
345
+ )
346
+ # gRPC-bidi doesn't have the concept of node_id,
347
+ # so we set it to -1
348
+ node_state = NodeState(
349
+ node_id=-1,
350
+ node_config={},
351
+ )
352
+ else:
353
+ # Call create_node fn to register node
354
+ node_id: Optional[int] = ( # pylint: disable=assignment-from-none
355
+ create_node()
356
+ ) # pylint: disable=not-callable
357
+ if node_id is None:
358
+ raise ValueError("Node registration failed")
359
+ node_state = NodeState(
360
+ node_id=node_id,
361
+ node_config=node_config,
362
+ )
340
363
 
341
364
  app_state_tracker.register_signal_handler()
342
365
  while not app_state_tracker.interrupt:
@@ -580,7 +603,7 @@ def _init_connection(transport: Optional[str], server_address: str) -> Tuple[
580
603
  Tuple[
581
604
  Callable[[], Optional[Message]],
582
605
  Callable[[Message], None],
583
- Optional[Callable[[], None]],
606
+ Optional[Callable[[], Optional[int]]],
584
607
  Optional[Callable[[], None]],
585
608
  Optional[Callable[[int], Run]],
586
609
  ]
flwr/client/client_app.py CHANGED
@@ -30,21 +30,41 @@ from flwr.common.logger import warn_deprecated_feature, warn_preview_feature
30
30
  from .typing import ClientAppCallable
31
31
 
32
32
 
33
+ def _alert_erroneous_client_fn() -> None:
34
+ raise ValueError(
35
+ "A `ClientApp` cannot make use of a `client_fn` that does "
36
+ "not have a signature in the form: `def client_fn(context: "
37
+ "Context)`. You can import the `Context` like this: "
38
+ "`from flwr.common import Context`"
39
+ )
40
+
41
+
33
42
  def _inspect_maybe_adapt_client_fn_signature(client_fn: ClientFnExt) -> ClientFnExt:
34
43
  client_fn_args = inspect.signature(client_fn).parameters
44
+ first_arg = list(client_fn_args.keys())[0]
45
+
46
+ if len(client_fn_args) != 1:
47
+ _alert_erroneous_client_fn()
48
+
49
+ first_arg_type = client_fn_args[first_arg].annotation
35
50
 
36
- if not all(key in client_fn_args for key in ["node_id", "partition_id"]):
51
+ if first_arg_type is str or first_arg == "cid":
52
+ # Warn previous signature for `client_fn` seems to be used
37
53
  warn_deprecated_feature(
38
- "`client_fn` now expects a signature `def client_fn(node_id: int, "
39
- "partition_id: Optional[int])`.\nYou provided `client_fn` with signature: "
40
- f"{dict(client_fn_args.items())}"
54
+ "`client_fn` now expects a signature `def client_fn(context: Context)`."
55
+ "The provided `client_fn` has signature: "
56
+ f"{dict(client_fn_args.items())}. You can import the `Context` like this:"
57
+ " `from flwr.common import Context`"
41
58
  )
42
59
 
43
60
  # Wrap depcreated client_fn inside a function with the expected signature
44
61
  def adaptor_fn(
45
- node_id: int, partition_id: Optional[int] # pylint: disable=unused-argument
46
- ) -> Client:
47
- return client_fn(str(partition_id)) # type: ignore
62
+ context: Context,
63
+ ) -> Client: # pylint: disable=unused-argument
64
+ # if patition-id is defined, pass it. Else pass node_id that should
65
+ # always be defined during Context init.
66
+ cid = context.node_config.get("partition-id", context.node_id)
67
+ return client_fn(str(cid)) # type: ignore
48
68
 
49
69
  return adaptor_fn
50
70
 
@@ -71,7 +91,7 @@ class ClientApp:
71
91
  >>> class FlowerClient(NumPyClient):
72
92
  >>> # ...
73
93
  >>>
74
- >>> def client_fn(node_id: int, partition_id: Optional[int]):
94
+ >>> def client_fn(context: Context):
75
95
  >>> return FlowerClient().to_client()
76
96
  >>>
77
97
  >>> app = ClientApp(client_fn)
@@ -44,7 +44,7 @@ def grpc_adapter( # pylint: disable=R0913
44
44
  Tuple[
45
45
  Callable[[], Optional[Message]],
46
46
  Callable[[Message], None],
47
- Optional[Callable[[], None]],
47
+ Optional[Callable[[], Optional[int]]],
48
48
  Optional[Callable[[], None]],
49
49
  Optional[Callable[[int], Run]],
50
50
  ]
@@ -72,7 +72,7 @@ def grpc_connection( # pylint: disable=R0913, R0915
72
72
  Tuple[
73
73
  Callable[[], Optional[Message]],
74
74
  Callable[[Message], None],
75
- Optional[Callable[[], None]],
75
+ Optional[Callable[[], Optional[int]]],
76
76
  Optional[Callable[[], None]],
77
77
  Optional[Callable[[int], Run]],
78
78
  ]
@@ -79,7 +79,7 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
79
79
  Tuple[
80
80
  Callable[[], Optional[Message]],
81
81
  Callable[[Message], None],
82
- Optional[Callable[[], None]],
82
+ Optional[Callable[[], Optional[int]]],
83
83
  Optional[Callable[[], None]],
84
84
  Optional[Callable[[int], Run]],
85
85
  ]
@@ -176,7 +176,7 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
176
176
  if not ping_stop_event.is_set():
177
177
  ping_stop_event.wait(next_interval)
178
178
 
179
- def create_node() -> None:
179
+ def create_node() -> Optional[int]:
180
180
  """Set create_node."""
181
181
  # Call FleetAPI
182
182
  create_node_request = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
@@ -189,6 +189,7 @@ def grpc_request_response( # pylint: disable=R0913, R0914, R0915
189
189
  nonlocal node, ping_thread
190
190
  node = cast(Node, create_node_response.node)
191
191
  ping_thread = start_ping_loop(ping, ping_stop_event)
192
+ return node.node_id
192
193
 
193
194
  def delete_node() -> None:
194
195
  """Set delete_node."""
@@ -92,7 +92,7 @@ def handle_legacy_message_from_msgtype(
92
92
  client_fn: ClientFnExt, message: Message, context: Context
93
93
  ) -> Message:
94
94
  """Handle legacy message in the inner most mod."""
95
- client = client_fn(message.metadata.dst_node_id, context.partition_id)
95
+ client = client_fn(context)
96
96
 
97
97
  # Check if NumPyClient is returend
98
98
  if isinstance(client, NumPyClient):
flwr/client/node_state.py CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  from dataclasses import dataclass
19
19
  from pathlib import Path
20
- from typing import Any, Dict, Optional
20
+ from typing import Dict, Optional
21
21
 
22
22
  from flwr.common import Context, RecordSet
23
23
  from flwr.common.config import get_fused_config
@@ -35,10 +35,14 @@ class RunInfo:
35
35
  class NodeState:
36
36
  """State of a node where client nodes execute runs."""
37
37
 
38
- def __init__(self, partition_id: Optional[int]) -> None:
39
- self._meta: Dict[str, Any] = {} # holds metadata about the node
38
+ def __init__(
39
+ self,
40
+ node_id: int,
41
+ node_config: Dict[str, str],
42
+ ) -> None:
43
+ self.node_id = node_id
44
+ self.node_config = node_config
40
45
  self.run_infos: Dict[int, RunInfo] = {}
41
- self._partition_id = partition_id
42
46
 
43
47
  def register_context(
44
48
  self,
@@ -52,9 +56,10 @@ class NodeState:
52
56
  self.run_infos[run_id] = RunInfo(
53
57
  initial_run_config=initial_run_config,
54
58
  context=Context(
59
+ node_id=self.node_id,
60
+ node_config=self.node_config,
55
61
  state=RecordSet(),
56
62
  run_config=initial_run_config.copy(),
57
- partition_id=self._partition_id,
58
63
  ),
59
64
  )
60
65
 
@@ -41,7 +41,7 @@ def test_multirun_in_node_state() -> None:
41
41
  expected_values = {0: "1", 1: "1" * 3, 2: "1" * 2, 3: "1", 5: "1"}
42
42
 
43
43
  # NodeState
44
- node_state = NodeState(partition_id=None)
44
+ node_state = NodeState(node_id=0, node_config={})
45
45
 
46
46
  for task in tasks:
47
47
  run_id = task.run_id
@@ -90,7 +90,7 @@ def http_request_response( # pylint: disable=,R0913, R0914, R0915
90
90
  Tuple[
91
91
  Callable[[], Optional[Message]],
92
92
  Callable[[Message], None],
93
- Optional[Callable[[], None]],
93
+ Optional[Callable[[], Optional[int]]],
94
94
  Optional[Callable[[], None]],
95
95
  Optional[Callable[[int], Run]],
96
96
  ]
@@ -237,19 +237,20 @@ def http_request_response( # pylint: disable=,R0913, R0914, R0915
237
237
  if not ping_stop_event.is_set():
238
238
  ping_stop_event.wait(next_interval)
239
239
 
240
- def create_node() -> None:
240
+ def create_node() -> Optional[int]:
241
241
  """Set create_node."""
242
242
  req = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
243
243
 
244
244
  # Send the request
245
245
  res = _request(req, CreateNodeResponse, PATH_CREATE_NODE)
246
246
  if res is None:
247
- return
247
+ return None
248
248
 
249
249
  # Remember the node and the ping-loop thread
250
250
  nonlocal node, ping_thread
251
251
  node = res.node
252
252
  ping_thread = start_ping_loop(ping, ping_stop_event)
253
+ return node.node_id
253
254
 
254
255
  def delete_node() -> None:
255
256
  """Set delete_node."""
@@ -29,7 +29,12 @@ from cryptography.hazmat.primitives.serialization import (
29
29
 
30
30
  from flwr.client.client_app import ClientApp, LoadClientAppError
31
31
  from flwr.common import EventType, event
32
- from flwr.common.config import get_flwr_dir, get_project_config, get_project_dir
32
+ from flwr.common.config import (
33
+ get_flwr_dir,
34
+ get_project_config,
35
+ get_project_dir,
36
+ parse_config_args,
37
+ )
33
38
  from flwr.common.constant import (
34
39
  TRANSPORT_TYPE_GRPC_ADAPTER,
35
40
  TRANSPORT_TYPE_GRPC_RERE,
@@ -67,7 +72,7 @@ def run_supernode() -> None:
67
72
  authentication_keys=authentication_keys,
68
73
  max_retries=args.max_retries,
69
74
  max_wait_time=args.max_wait_time,
70
- partition_id=args.partition_id,
75
+ node_config=parse_config_args(args.node_config),
71
76
  flwr_dir=get_flwr_dir(args.flwr_dir),
72
77
  )
73
78
 
@@ -93,6 +98,7 @@ def run_client_app() -> None:
93
98
 
94
99
  _start_client_internal(
95
100
  server_address=args.superlink,
101
+ node_config=parse_config_args(args.node_config),
96
102
  load_client_app_fn=load_fn,
97
103
  transport=args.transport,
98
104
  root_certificates=root_certificates,
@@ -389,11 +395,11 @@ def _parse_args_common(parser: argparse.ArgumentParser) -> None:
389
395
  help="The SuperNode's public key (as a path str) to enable authentication.",
390
396
  )
391
397
  parser.add_argument(
392
- "--partition-id",
393
- type=int,
394
- help="The data partition index associated with this SuperNode. Better suited "
395
- "for prototyping purposes where a SuperNode might only load a fraction of an "
396
- "artificially partitioned dataset (e.g. using `flwr-datasets`)",
398
+ "--node-config",
399
+ type=str,
400
+ help="A comma separated list of key/value pairs (separated by `=`) to "
401
+ "configure the SuperNode. "
402
+ "E.g. --node-config 'key1=\"value1\",partition-id=0,num-partitions=100'",
397
403
  )
398
404
 
399
405
 
flwr/client/typing.py CHANGED
@@ -15,7 +15,7 @@
15
15
  """Custom types for Flower clients."""
16
16
 
17
17
 
18
- from typing import Callable, Optional
18
+ from typing import Callable
19
19
 
20
20
  from flwr.common import Context, Message
21
21
 
@@ -23,7 +23,7 @@ from .client import Client as Client
23
23
 
24
24
  # Compatibility
25
25
  ClientFn = Callable[[str], Client]
26
- ClientFnExt = Callable[[int, Optional[int]], Client]
26
+ ClientFnExt = Callable[[Context], Client]
27
27
 
28
28
  ClientAppCallable = Callable[[Message, Context], Message]
29
29
  Mod = Callable[[Message, Context, ClientAppCallable], Message]
flwr/common/config.py CHANGED
@@ -121,16 +121,16 @@ def flatten_dict(raw_dict: Dict[str, Any], parent_key: str = "") -> Dict[str, st
121
121
 
122
122
 
123
123
  def parse_config_args(
124
- config_overrides: Optional[str],
124
+ config: Optional[str],
125
125
  separator: str = ",",
126
126
  ) -> Dict[str, str]:
127
127
  """Parse separator separated list of key-value pairs separated by '='."""
128
128
  overrides: Dict[str, str] = {}
129
129
 
130
- if config_overrides is None:
130
+ if config is None:
131
131
  return overrides
132
132
 
133
- overrides_list = config_overrides.split(separator)
133
+ overrides_list = config.split(separator)
134
134
  if (
135
135
  len(overrides_list) == 1
136
136
  and "=" not in overrides_list
flwr/common/constant.py CHANGED
@@ -57,6 +57,9 @@ APP_DIR = "apps"
57
57
  FAB_CONFIG_FILE = "pyproject.toml"
58
58
  FLWR_HOME = "FLWR_HOME"
59
59
 
60
+ # Constants entries in Node config for Simulation
61
+ PARTITION_ID_KEY = "partition-id"
62
+ NUM_PARTITIONS_KEY = "num-partitions"
60
63
 
61
64
  GRPC_ADAPTER_METADATA_FLOWER_VERSION_KEY = "flower-version"
62
65
  GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY = "should-exit"
flwr/common/context.py CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
 
18
18
  from dataclasses import dataclass
19
- from typing import Dict, Optional
19
+ from typing import Dict
20
20
 
21
21
  from .record import RecordSet
22
22
 
@@ -27,6 +27,11 @@ class Context:
27
27
 
28
28
  Parameters
29
29
  ----------
30
+ node_id : int
31
+ The ID that identifies the node.
32
+ node_config : Dict[str, str]
33
+ A config (key/value mapping) unique to the node and independent of the
34
+ `run_config`. This config persists across all runs this node participates in.
30
35
  state : RecordSet
31
36
  Holds records added by the entity in a given run and that will stay local.
32
37
  This means that the data it holds will never leave the system it's running from.
@@ -38,22 +43,21 @@ class Context:
38
43
  A config (key/value mapping) held by the entity in a given run and that will
39
44
  stay local. It can be used at any point during the lifecycle of this entity
40
45
  (e.g. across multiple rounds)
41
- partition_id : Optional[int] (default: None)
42
- An index that specifies the data partition that the ClientApp using this Context
43
- object should make use of. Setting this attribute is better suited for
44
- simulation or proto typing setups.
45
46
  """
46
47
 
48
+ node_id: int
49
+ node_config: Dict[str, str]
47
50
  state: RecordSet
48
- partition_id: Optional[int]
49
51
  run_config: Dict[str, str]
50
52
 
51
- def __init__(
53
+ def __init__( # pylint: disable=too-many-arguments
52
54
  self,
55
+ node_id: int,
56
+ node_config: Dict[str, str],
53
57
  state: RecordSet,
54
58
  run_config: Dict[str, str],
55
- partition_id: Optional[int] = None,
56
59
  ) -> None:
60
+ self.node_id = node_id
61
+ self.node_config = node_config
57
62
  self.state = state
58
63
  self.run_config = run_config
59
- self.partition_id = partition_id
@@ -52,4 +52,4 @@ class LegacyContext(Context):
52
52
  self.strategy = strategy
53
53
  self.client_manager = client_manager
54
54
  self.history = History()
55
- super().__init__(state, run_config={})
55
+ super().__init__(node_id=0, node_config={}, state=state, run_config={})
@@ -78,7 +78,9 @@ def run(
78
78
  server_app = _load()
79
79
 
80
80
  # Initialize Context
81
- context = Context(state=RecordSet(), run_config=server_app_run_config)
81
+ context = Context(
82
+ node_id=0, node_config={}, state=RecordSet(), run_config=server_app_run_config
83
+ )
82
84
 
83
85
  # Call ServerApp
84
86
  server_app(driver=driver, context=context)
@@ -21,6 +21,7 @@ from typing import Callable, Dict, List, Tuple, Union
21
21
  import ray
22
22
 
23
23
  from flwr.client.client_app import ClientApp
24
+ from flwr.common.constant import PARTITION_ID_KEY
24
25
  from flwr.common.context import Context
25
26
  from flwr.common.logger import log
26
27
  from flwr.common.message import Message
@@ -168,7 +169,7 @@ class RayBackend(Backend):
168
169
 
169
170
  Return output message and updated context.
170
171
  """
171
- partition_id = context.partition_id
172
+ partition_id = context.node_config[PARTITION_ID_KEY]
172
173
 
173
174
  try:
174
175
  # Submit a task to the pool
@@ -29,7 +29,12 @@ from typing import Callable, Dict, Optional
29
29
 
30
30
  from flwr.client.client_app import ClientApp, ClientAppException, LoadClientAppError
31
31
  from flwr.client.node_state import NodeState
32
- from flwr.common.constant import PING_MAX_INTERVAL, ErrorCode
32
+ from flwr.common.constant import (
33
+ NUM_PARTITIONS_KEY,
34
+ PARTITION_ID_KEY,
35
+ PING_MAX_INTERVAL,
36
+ ErrorCode,
37
+ )
33
38
  from flwr.common.logger import log
34
39
  from flwr.common.message import Error
35
40
  from flwr.common.object_ref import load_app
@@ -73,7 +78,7 @@ def worker(
73
78
  task_ins: TaskIns = taskins_queue.get(timeout=1.0)
74
79
  node_id = task_ins.task.consumer.node_id
75
80
 
76
- # Register and retrieve runstate
81
+ # Register and retrieve context
77
82
  node_states[node_id].register_context(run_id=task_ins.run_id)
78
83
  context = node_states[node_id].retrieve_context(run_id=task_ins.run_id)
79
84
 
@@ -283,8 +288,16 @@ def start_vce(
283
288
 
284
289
  # Construct mapping of NodeStates
285
290
  node_states: Dict[int, NodeState] = {}
291
+ # Number of unique partitions
292
+ num_partitions = len(set(nodes_mapping.values()))
286
293
  for node_id, partition_id in nodes_mapping.items():
287
- node_states[node_id] = NodeState(partition_id=partition_id)
294
+ node_states[node_id] = NodeState(
295
+ node_id=node_id,
296
+ node_config={
297
+ PARTITION_ID_KEY: str(partition_id),
298
+ NUM_PARTITIONS_KEY: str(num_partitions),
299
+ },
300
+ )
288
301
 
289
302
  # Load backend config
290
303
  log(DEBUG, "Supported backends: %s", list(supported_backends.keys()))
flwr/simulation/app.py CHANGED
@@ -111,9 +111,9 @@ def start_simulation(
111
111
  Parameters
112
112
  ----------
113
113
  client_fn : ClientFnExt
114
- A function creating Client instances. The function must have the signature
115
- `client_fn(node_id: int, partition_id: Optional[int]). It should return
116
- a single client instance of type Client. Note that the created client
114
+ A function creating `Client` instances. The function must have the signature
115
+ `client_fn(context: Context). It should return
116
+ a single client instance of type `Client`. Note that the created client
117
117
  instances are ephemeral and will often be destroyed after a single method
118
118
  invocation. Since client instances are not long-lived, they should not attempt
119
119
  to carry state over method invocations. Any state required by the instance
@@ -327,6 +327,7 @@ def start_simulation(
327
327
  client_fn=client_fn,
328
328
  node_id=node_id,
329
329
  partition_id=partition_id,
330
+ num_partitions=num_clients,
330
331
  actor_pool=pool,
331
332
  )
332
333
  initialized_server.client_manager().register(client=client_proxy)
@@ -24,7 +24,12 @@ from flwr.client import ClientFnExt
24
24
  from flwr.client.client_app import ClientApp
25
25
  from flwr.client.node_state import NodeState
26
26
  from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
27
- from flwr.common.constant import MessageType, MessageTypeLegacy
27
+ from flwr.common.constant import (
28
+ NUM_PARTITIONS_KEY,
29
+ PARTITION_ID_KEY,
30
+ MessageType,
31
+ MessageTypeLegacy,
32
+ )
28
33
  from flwr.common.logger import log
29
34
  from flwr.common.recordset_compat import (
30
35
  evaluateins_to_recordset,
@@ -43,11 +48,12 @@ from flwr.simulation.ray_transport.ray_actor import VirtualClientEngineActorPool
43
48
  class RayActorClientProxy(ClientProxy):
44
49
  """Flower client proxy which delegates work using Ray."""
45
50
 
46
- def __init__(
51
+ def __init__( # pylint: disable=too-many-arguments
47
52
  self,
48
53
  client_fn: ClientFnExt,
49
54
  node_id: int,
50
55
  partition_id: int,
56
+ num_partitions: int,
51
57
  actor_pool: VirtualClientEngineActorPool,
52
58
  ):
53
59
  super().__init__(cid=str(node_id))
@@ -59,7 +65,13 @@ class RayActorClientProxy(ClientProxy):
59
65
 
60
66
  self.app_fn = _load_app
61
67
  self.actor_pool = actor_pool
62
- self.proxy_state = NodeState(partition_id=self.partition_id)
68
+ self.proxy_state = NodeState(
69
+ node_id=node_id,
70
+ node_config={
71
+ PARTITION_ID_KEY: str(partition_id),
72
+ NUM_PARTITIONS_KEY: str(num_partitions),
73
+ },
74
+ )
63
75
 
64
76
  def _submit_job(self, message: Message, timeout: Optional[float]) -> Message:
65
77
  """Sumbit a message to the ActorPool."""
@@ -68,18 +80,19 @@ class RayActorClientProxy(ClientProxy):
68
80
  # Register state
69
81
  self.proxy_state.register_context(run_id=run_id)
70
82
 
71
- # Retrieve state
72
- state = self.proxy_state.retrieve_context(run_id=run_id)
83
+ # Retrieve context
84
+ context = self.proxy_state.retrieve_context(run_id=run_id)
85
+ partition_id_str = context.node_config[PARTITION_ID_KEY]
73
86
 
74
87
  try:
75
88
  self.actor_pool.submit_client_job(
76
- lambda a, a_fn, mssg, partition_id, state: a.run.remote(
77
- a_fn, mssg, partition_id, state
89
+ lambda a, a_fn, mssg, partition_id, context: a.run.remote(
90
+ a_fn, mssg, partition_id, context
78
91
  ),
79
- (self.app_fn, message, str(self.partition_id), state),
92
+ (self.app_fn, message, partition_id_str, context),
80
93
  )
81
94
  out_mssg, updated_context = self.actor_pool.get_client_result(
82
- str(self.partition_id), timeout
95
+ partition_id_str, timeout
83
96
  )
84
97
 
85
98
  # Update state
flwr/superexec/app.py CHANGED
@@ -127,11 +127,11 @@ def _try_obtain_certificates(
127
127
  return None
128
128
  # Check if certificates are provided
129
129
  if args.ssl_certfile and args.ssl_keyfile and args.ssl_ca_certfile:
130
- if not Path.is_file(args.ssl_ca_certfile):
130
+ if not Path(args.ssl_ca_certfile).is_file():
131
131
  sys.exit("Path argument `--ssl-ca-certfile` does not point to a file.")
132
- if not Path.is_file(args.ssl_certfile):
132
+ if not Path(args.ssl_certfile).is_file():
133
133
  sys.exit("Path argument `--ssl-certfile` does not point to a file.")
134
- if not Path.is_file(args.ssl_keyfile):
134
+ if not Path(args.ssl_keyfile).is_file():
135
135
  sys.exit("Path argument `--ssl-keyfile` does not point to a file.")
136
136
  certificates = (
137
137
  Path(args.ssl_ca_certfile).read_bytes(), # CA certificate
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.10.0.dev20240711
3
+ Version: 1.10.0.dev20240713
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -14,13 +14,13 @@ flwr/cli/new/templates/app/README.md.tpl,sha256=_qGtgpKYKoCJVjQnvlBMKvFs_1gzTcL9
14
14
  flwr/cli/new/templates/app/__init__.py,sha256=DU7QMY7IhMQyuwm_tja66xU0KXTWQFqzfTqwg-_NJdE,729
15
15
  flwr/cli/new/templates/app/code/__init__.py,sha256=EM6vfvgAILKPaPn7H1wMV1Wi01WyZCP_Eg6NxD6oWg8,736
16
16
  flwr/cli/new/templates/app/code/__init__.py.tpl,sha256=olwrBeJemHNBWvjc6gJURloFRqW40dAy7FRQA5pDqHU,21
17
- flwr/cli/new/templates/app/code/client.hf.py.tpl,sha256=RaN89A8HgKp6kjhzH8tgtDSWW8BwwcvJdqRLcvG04zw,1450
18
- flwr/cli/new/templates/app/code/client.jax.py.tpl,sha256=MtxhwQzxAWNvlz8B-L-2a8LXcgaLPW8dp5K0vBZHR_o,1434
19
- flwr/cli/new/templates/app/code/client.mlx.py.tpl,sha256=53wJy6s3zk4CZwob_qPmMoOqJ-LZNKbdDe_hw5LwOXE,2113
20
- flwr/cli/new/templates/app/code/client.numpy.py.tpl,sha256=mTh7Y_jOJrPUvDYHVJy4wJCnjXZV_q-jlDkB07U5GSk,521
21
- flwr/cli/new/templates/app/code/client.pytorch.py.tpl,sha256=MgCtMSv1Th16Faod11HubVaARkLYt7vS9RYH962-2pk,1172
22
- flwr/cli/new/templates/app/code/client.sklearn.py.tpl,sha256=S71SZiHaRXtKqUk3m5Elc_c6HhKAIKLalrKOQ3p20No,2801
23
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=dxrTO9JwYrDBjLsmCiRLetN9KxbnWRTeGA0BQbnOu_A,1280
17
+ flwr/cli/new/templates/app/code/client.hf.py.tpl,sha256=cqq2M-l8yrsw6kZZLIjum2g-0uKg4Pyh3YGhgpQKQrw,1636
18
+ flwr/cli/new/templates/app/code/client.jax.py.tpl,sha256=i_SZykD42vqEvv2ZyX655szuikXJXLc6uV1T-LWiYLU,1479
19
+ flwr/cli/new/templates/app/code/client.mlx.py.tpl,sha256=Ntpbz9fcw-87FPBZmhns1NbJJXOQxnssJ_lStAqGuaQ,2299
20
+ flwr/cli/new/templates/app/code/client.numpy.py.tpl,sha256=ov9mtWJGjaQ9ZVlQ5jsuCjHDeETf13GFla5jbP3KimE,561
21
+ flwr/cli/new/templates/app/code/client.pytorch.py.tpl,sha256=bmmplNEVk3uHERFGlDP6FmBJQ7IBFw-OQ8otueAmOX0,1358
22
+ flwr/cli/new/templates/app/code/client.sklearn.py.tpl,sha256=C2PfCn0w8MpXmY4tuIIJ7fF2Mv-YW37YmMf0knIEARo,2905
23
+ flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=D8h_RSWk2GGazW2-fLle1dIz9ABWuC_1aczxNbKHfOA,1390
24
24
  flwr/cli/new/templates/app/code/flwr_tune/__init__.py,sha256=JgNgBtKdm1jKM9625WxappCAVUGtYAmcjKSsXJ1u3ZQ,748
25
25
  flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl,sha256=KXqCaD2NSEaLHT08fgQTzWlrtOdfxRYs0fsLw9yecCY,2647
26
26
  flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl,sha256=MvQ5tt1r7CBUR8y-nBcZs4as2m1YimxegLYw_nHmXzc,4048
@@ -36,10 +36,10 @@ flwr/cli/new/templates/app/code/server.numpy.py.tpl,sha256=fRxrDXV7pB1aDhQUXMBmr
36
36
  flwr/cli/new/templates/app/code/server.pytorch.py.tpl,sha256=ltdsnFSvFGPcycVmRL4ITlr-TV0CmmXcperZe7Vamow,593
37
37
  flwr/cli/new/templates/app/code/server.sklearn.py.tpl,sha256=cLzOpQzGIUzEazuFsjBpXAQUNPy6in6zR33SCqhix6o,341
38
38
  flwr/cli/new/templates/app/code/server.tensorflow.py.tpl,sha256=gsNrWCKTU77_65_gw9nlp1LSQojgP5QQIWILvqdjx2s,579
39
- flwr/cli/new/templates/app/code/task.hf.py.tpl,sha256=Rw8cnds4Ym8o8TOq6kMkwlBJfIfvsfnb02jwyulOgF8,2857
39
+ flwr/cli/new/templates/app/code/task.hf.py.tpl,sha256=B5CrA7L5PSOWnluYoAAL7LCeKvP8t-Rhwt6t2ZTYP3g,2873
40
40
  flwr/cli/new/templates/app/code/task.jax.py.tpl,sha256=u4o3V019EH79szOw2xzVeC5r9xgQiayPi9ZTIopV2TA,1519
41
- flwr/cli/new/templates/app/code/task.mlx.py.tpl,sha256=y7aVj3F_98-wBnDcbPsCNnFs9BOHTn0y6XIYkByzv7Y,2598
42
- flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=NvajdZN-eTyfdqKK0v2MrvWITXw9BjJ3Ri5c1haPJDs,3684
41
+ flwr/cli/new/templates/app/code/task.mlx.py.tpl,sha256=nrfZ1aGOs_ayb70j7XdAmwFYa-rN10d9GIMIKLzctUE,2614
42
+ flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=TU4uNtJ9wtxeVvoHD3_K89EFWmrIvdECdASzRX-4Uvk,3694
43
43
  flwr/cli/new/templates/app/code/task.tensorflow.py.tpl,sha256=cPOUUS07QbblT9PGFucwu9lY1clRA4-W4DQGA7cpcao,1044
44
44
  flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=5kAtRcTvn2-s2i2_WGoDNJQ__ALADQjabX7lQlCNOGA,899
45
45
  flwr/cli/new/templates/app/pyproject.hf.toml.tpl,sha256=O3-dgH8_knk9uM49IzX06CYC2Ev5xdPuITB40Phvewc,759
@@ -53,21 +53,21 @@ flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
53
53
  flwr/cli/run/run.py,sha256=eFYZwHOw9pHo_jxtS-UQIf7LVIOiNwjnJdaykcZQz5Q,4969
54
54
  flwr/cli/utils.py,sha256=l65Ul0YsSBPuypk0uorAtEDmLEYiUrzpCXi6zCg9mJ4,4506
55
55
  flwr/client/__init__.py,sha256=wzJZsYJIHf_8-PMzvfbinyzzjgh1UP1vLrAw2_yEbKI,1345
56
- flwr/client/app.py,sha256=TC9wO6dSuXxRt0YsqFTz8Usf-ophSww9cwMRjEaXKjI,25123
56
+ flwr/client/app.py,sha256=5v5EsA1zbViJAp998dCVRXvsyigZ-x3JEIKQ_fLeA48,26102
57
57
  flwr/client/client.py,sha256=Vp9UkOkoHdNfn6iMYZsj_5m_GICiFfUlKEVaLad-YhM,8183
58
- flwr/client/client_app.py,sha256=cvY-km3JEOWKxUio4xvksNFBk2FQQXliUfQTlDty71w,9648
58
+ flwr/client/client_app.py,sha256=WcO4r6wrdfaus__3s22D2sYjfcptdgmVujUAYdNE6HU,10393
59
59
  flwr/client/dpfedavg_numpy_client.py,sha256=ylZ-LpBIKmL1HCiS8kq4pkp2QGalc8rYEzDHdRG3VRQ,7435
60
60
  flwr/client/grpc_adapter_client/__init__.py,sha256=QyNWIbsq9DpyMk7oemiO1P3TBFfkfkctnJ1JoAkTl3s,742
61
- flwr/client/grpc_adapter_client/connection.py,sha256=jxwWnqppwpwI7c5xtGRwbiyDKRLdMP4Y6YLAwTypfeY,3864
61
+ flwr/client/grpc_adapter_client/connection.py,sha256=fM6QTIrcDGIXux7nzxOY3qNUfBGxVl1W7861iGSy5wA,3873
62
62
  flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
63
- flwr/client/grpc_client/connection.py,sha256=5KZ1AhKJiJsMgLoAwsyN83k8hu_XWaRbUb-ATR1RvF0,9302
63
+ flwr/client/grpc_client/connection.py,sha256=7J3YlvvBrky3f8UJ99U9IsPECGtcY8rvXsyr_Ol8qyY,9311
64
64
  flwr/client/grpc_rere_client/__init__.py,sha256=MK-oSoV3kwUEQnIwl0GN4OpiHR7eLOrMA8ikunET130,752
65
65
  flwr/client/grpc_rere_client/client_interceptor.py,sha256=sYPEznuQPdy2BPDlvM9FK0ZRRucb4NfwUee1Z_mN82E,4954
66
- flwr/client/grpc_rere_client/connection.py,sha256=ieIOBSrrjWVZflbLdb6KMehzs-aOdvIhcTTtfgQbT5I,10292
66
+ flwr/client/grpc_rere_client/connection.py,sha256=nC4Usb1qok2liVxwlbQokUNwRRRD10_GIscbspqqz_c,10338
67
67
  flwr/client/grpc_rere_client/grpc_adapter.py,sha256=woljH8yr1pyLH4W4Azogyy7Nafn6y9DHBnDCIIVKwCw,4711
68
68
  flwr/client/heartbeat.py,sha256=cx37mJBH8LyoIN4Lks85wtqT1mnU5GulQnr4pGCvAq0,2404
69
69
  flwr/client/message_handler/__init__.py,sha256=QxxQuBNpFPTHx3KiUNvQSlqMKlEnbRR1kFfc1KVje08,719
70
- flwr/client/message_handler/message_handler.py,sha256=rLuzcpRqSFofQ8QIB6OGnh9HC5uxUAnkQQIJaP0JHvM,6574
70
+ flwr/client/message_handler/message_handler.py,sha256=q1fiWQob4TCaSHxvIklEtqsz-TwupXNYK-yKV9LubWY,6531
71
71
  flwr/client/message_handler/task_handler.py,sha256=ZDJBKmrn2grRMNl1rU1iGs7FiMHL5VmZiSp_6h9GHVU,1824
72
72
  flwr/client/mod/__init__.py,sha256=37XeXZLFq_tzFVKVtC9JaigM2bSAU7BrGQvMPCE3Q28,1159
73
73
  flwr/client/mod/centraldp_mods.py,sha256=UGwNuqpmOWfLdfJITFgdi1TG-nLjuSb-cbEyoyfDgxQ,5415
@@ -77,19 +77,19 @@ flwr/client/mod/secure_aggregation/__init__.py,sha256=A7DzZ3uvXTUkuHBzrxJMWQQD4R
77
77
  flwr/client/mod/secure_aggregation/secagg_mod.py,sha256=wI9tuIEvMUETz-wVIEbPYvh-1nK9CEylBLGoVpNhL94,1095
78
78
  flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=fZTfIELkYS64lpgxQKL66s-QHjCn-159qfLoNoIMJjc,19699
79
79
  flwr/client/mod/utils.py,sha256=UAJXiB0wwVyLkCkpW_i5BXikdBR65p8sNFr7VNHm2nk,1226
80
- flwr/client/node_state.py,sha256=8nsvz8IndKLI1VeAzO7EwHh4rZdvaw03W0llLQhifiw,2830
81
- flwr/client/node_state_tests.py,sha256=vXxS3vHMQxl66SfD2MO-JNi83EabYs8Jhd8N7H2zfEM,2231
80
+ flwr/client/node_state.py,sha256=_qgoNJXay25maUmcBwUcSVYpKTiW0RaoiWDSL4oFvJE,2864
81
+ flwr/client/node_state_tests.py,sha256=-4fVsn7y-z9NYBuhq-cjepgxgVuPqqQgDOL4SofrdIo,2239
82
82
  flwr/client/numpy_client.py,sha256=u76GWAdHmJM88Agm2EgLQSvO8Jnk225mJTk-_TmPjFE,10283
83
83
  flwr/client/rest_client/__init__.py,sha256=5KGlp7pjc1dhNRkKlaNtUfQmg8wrRFh9lS3P3uRS-7Q,735
84
- flwr/client/rest_client/connection.py,sha256=nowX8_TMnaiIhBMU5f60sIOkvcS3DHOHBT_YrvCnxnw,12096
84
+ flwr/client/rest_client/connection.py,sha256=aY_UzrNyE8g-xPAK_POZZZ93mERHTe-pOhNP-uZ8GyU,12147
85
85
  flwr/client/supernode/__init__.py,sha256=SUhWOzcgXRNXk1V9UgB5-FaWukqqrOEajVUHEcPkwyQ,865
86
- flwr/client/supernode/app.py,sha256=GQ9N2ydrTkgo7ZVb3AFOWYeLSru3qwQ_SvsYU6fdhI4,15321
87
- flwr/client/typing.py,sha256=RJGVF64Z0nqW-qmdFuFaY4Jig3dMUFgNhFi-5dq-8-I,1069
86
+ flwr/client/supernode/app.py,sha256=cg8momLzMnFrDBNJsSRDYrn8zHuJ-4UC3wIHf51aBXQ,15385
87
+ flwr/client/typing.py,sha256=dxoTBnTMfqXr5J7G3y-uNjqxYCddvxhu89spfj4Lm2U,1048
88
88
  flwr/common/__init__.py,sha256=4cBLNNnNTwHDnL_HCxhU5ILCSZ6fYh3A_aMBtlvHTVw,3721
89
89
  flwr/common/address.py,sha256=wRu1Luezx1PWadwV9OA_KNko01oVvbRnPqfzaDn8QOk,1882
90
- flwr/common/config.py,sha256=GTmXfeCi6Xt1CTUzg8TUshOHVv2vP8X8e6uCcLBWoX4,5024
91
- flwr/common/constant.py,sha256=qNmxEV3_pOO7MeTAA9qwIh4KoCPStcX3Gm8GRPIRx_4,2890
92
- flwr/common/context.py,sha256=dd37Q_0rngvGTzIwQ2M50jfhGGV0vV2-uGlL-gC4Y_Y,2170
90
+ flwr/common/config.py,sha256=7OF4R43st8teFXBWmV92noGEYVe00wT29QMs6od8mlI,4994
91
+ flwr/common/constant.py,sha256=1XxuRezsr9fl3xvQNPR2kyFkwNeG_f5vZayv0PFh0kY,3012
92
+ flwr/common/context.py,sha256=CQt4uzCDvCIr2WdkrWq0obAz92k2_ucXGrWtBZCxP_M,2256
93
93
  flwr/common/date.py,sha256=OcQuwpb2HxcblTqYm6H223ufop5UZw5N_fzalbpOVzY,891
94
94
  flwr/common/differential_privacy.py,sha256=WZWrL7C9XaB9l9NDkLDI5PvM7jwcoTTFu08ZVG8-M5Q,6113
95
95
  flwr/common/differential_privacy_constants.py,sha256=c7b7tqgvT7yMK0XN9ndiTBs4mQf6d3qk6K7KBZGlV4Q,1074
@@ -180,14 +180,14 @@ flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw
180
180
  flwr/server/compat/app.py,sha256=u0elxfiLjGouCMQIy5KnCpeCHdc3s0qvojUm8unInIs,3421
181
181
  flwr/server/compat/app_utils.py,sha256=B9pec7LnYACzowXKZTZNu3SNS-fSaHfefwvRyAQa4Nc,3456
182
182
  flwr/server/compat/driver_client_proxy.py,sha256=BxTDo7i89VAG2tuF4x7zogSVn2bXPMr0H2H0lERzW9c,5444
183
- flwr/server/compat/legacy_context.py,sha256=3T_vON4qXt31To0dd9ygULvxL9l1hmDSED6ZqBiLhxI,1781
183
+ flwr/server/compat/legacy_context.py,sha256=-c2J_tPDFahDILl80XFrgkdRLfpHWDu5W3DQP3nKdls,1814
184
184
  flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
185
185
  flwr/server/driver/__init__.py,sha256=bikRv6CjTwSvYh7tf10gziU5o2YotOWhhftz2tr3KDc,886
186
186
  flwr/server/driver/driver.py,sha256=NT_yaeit7_kZEIsCEqOWPID1GrVD3ywH4xZ2wtIh5lM,5217
187
187
  flwr/server/driver/grpc_driver.py,sha256=4Azmzq4RWzcLbOqBBEF-I78krWVWZ6bT0U42S25zMvY,9659
188
188
  flwr/server/driver/inmemory_driver.py,sha256=RcK94_NtjGZ4aZDIscnU7A3Uv1u8jGx29-xcbjQvZTM,6444
189
189
  flwr/server/history.py,sha256=bBOHKyX1eQONIsUx4EUU-UnAk1i0EbEl8ioyMq_UWQ8,5063
190
- flwr/server/run_serverapp.py,sha256=s8KyWbANv9kyj8_tJoDiLkUj9D6QrPWfC5M_xDCOtYU,9445
190
+ flwr/server/run_serverapp.py,sha256=iq62xuSPYndmsYiJRpRkT9cTfQpZ0FgsWLncktUvh98,9486
191
191
  flwr/server/server.py,sha256=wsXsxMZ9SQ0B42nBnUlcV83NJPycgrgg5bFwcQ4BYBE,17821
192
192
  flwr/server/server_app.py,sha256=1hul76ospG8L_KooK_ewn1sWPNTNYLTtZMeGNOBNruA,6267
193
193
  flwr/server/server_config.py,sha256=CZaHVAsMvGLjpWVcLPkiYxgJN4xfIyAiUrCI3fETKY4,1349
@@ -238,8 +238,8 @@ flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=yoSU-6nCJF9ASHGNpSY69nZ
238
238
  flwr/server/superlink/fleet/vce/__init__.py,sha256=36MHKiefnJeyjwMQzVUK4m06Ojon3WDcwZGQsAcyVhQ,783
239
239
  flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=oBIzmnrSSRvH_H0vRGEGWhWzQQwqe3zn6e13RsNwlIY,1466
240
240
  flwr/server/superlink/fleet/vce/backend/backend.py,sha256=iG3KSIY7DzNfcxmuLfTs7VdQJnqPCvvn5DFkTWKG5lI,2227
241
- flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=pIJm6YXZw-Jv3okRgred1yjm7b3EWuj1BvtyBd3Fk08,7422
242
- flwr/server/superlink/fleet/vce/vce_api.py,sha256=Aiv8fulgx4decS_PQ17L94VWc0Mxx7_lL4BmIX1-PMg,11745
241
+ flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=SnjZ1WOcrfMZNgiDdTHcFeXJqrY7UHx8kvO62mqU9S4,7489
242
+ flwr/server/superlink/fleet/vce/vce_api.py,sha256=jSMmo3AXN_6XlYyzasu5yZ2jir8_Avj2zD9qtMzun6Q,12055
243
243
  flwr/server/superlink/state/__init__.py,sha256=Gj2OTFLXvA-mAjBvwuKDM3rDrVaQPcIoybSa2uskMTE,1003
244
244
  flwr/server/superlink/state/in_memory_state.py,sha256=fb-f4RGiqXON0DC7aSEMNuNIjH406BhBYrNNX5Kza2g,13061
245
245
  flwr/server/superlink/state/sqlite_state.py,sha256=dO374mTkvhWQSiwbqwUXVnAYHev-j2mHaX9v8wFmmMA,29044
@@ -257,20 +257,20 @@ flwr/server/workflow/secure_aggregation/__init__.py,sha256=3XlgDOjD_hcukTGl6Bc1B
257
257
  flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=wpAkYPId0nfK6SgpUAtsCni4_MQLd-uqJ81tUKu3xlI,5838
258
258
  flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=BRqhlnVe8CYNoUvb_KCfRXay02NTT6a-pCrMaOqAxGc,29038
259
259
  flwr/simulation/__init__.py,sha256=9x8OCkK3jpFAPJB1aeEMOddz6V58bExQPtwE8Z3q-RY,1359
260
- flwr/simulation/app.py,sha256=8NDXoQ8oC11khXIGnydrsUh5JfaH7c2Fwzix8vDFK1I,15144
260
+ flwr/simulation/app.py,sha256=te3dQB3eodPwzsv1y4daPyaskIAaOtgoHaQLobrqoqY,15163
261
261
  flwr/simulation/ray_transport/__init__.py,sha256=wzcEEwUUlulnXsg6raCA1nGpP3LlAQDtJ8zNkCXcVbA,734
262
262
  flwr/simulation/ray_transport/ray_actor.py,sha256=3j0HgzjrlYjnzdTRy8aA4Nf6VoUvxi1hGRQkGSU5z6c,19020
263
- flwr/simulation/ray_transport/ray_client_proxy.py,sha256=zGLVebfwFhBo1CAqEQ0MtW-fPG8ark3e4n6OksFGch4,6954
263
+ flwr/simulation/ray_transport/ray_client_proxy.py,sha256=4KWWGSnfEBe3aGc0Ln5_1yRcZ52wKmOA7gXJKkMglvM,7302
264
264
  flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
265
265
  flwr/simulation/run_simulation.py,sha256=qGP8sHKAzJT9nGeqMw36iCsVXm4ZFMBisCORuTswr-g,17277
266
266
  flwr/superexec/__init__.py,sha256=9h94ogLxi6eJ3bUuJYq3E3pApThSabTPiSmPAGlTkHE,800
267
- flwr/superexec/app.py,sha256=dm0o3O6dlsk8hZHondat5QWrBh9UfKzHxn4dVrqwiRk,6151
267
+ flwr/superexec/app.py,sha256=1ZGSErFo3AQeIQOARKM1DN99fCuH451PeM1bDasBjRQ,6157
268
268
  flwr/superexec/deployment.py,sha256=xv5iQWuaMeeL0XE5KMLWq3gRU4lvsGu1-_oPIXi5x9E,3955
269
269
  flwr/superexec/exec_grpc.py,sha256=u-rztpOleqSGqgvNE-ZLw1HchNsBHU1-eB3m52GZ0pQ,1852
270
270
  flwr/superexec/exec_servicer.py,sha256=4R1f_9v0vly_bXpIYaXAeV1tO5LAy1AYygGGGNZmlQk,2194
271
271
  flwr/superexec/executor.py,sha256=TMQMMf-vv0htlv6v-eEBI67J1WL3Yz7dp_Fm1lgMEyU,1718
272
- flwr_nightly-1.10.0.dev20240711.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
273
- flwr_nightly-1.10.0.dev20240711.dist-info/METADATA,sha256=6FH6xn5sDwtX8aB4bz6zCf3au-O319V8I2iOIWWXDXE,15632
274
- flwr_nightly-1.10.0.dev20240711.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
275
- flwr_nightly-1.10.0.dev20240711.dist-info/entry_points.txt,sha256=7qBQcA-bDGDxnJmLd9FYqglFQubjCNqyg9M8a-lukps,336
276
- flwr_nightly-1.10.0.dev20240711.dist-info/RECORD,,
272
+ flwr_nightly-1.10.0.dev20240713.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
273
+ flwr_nightly-1.10.0.dev20240713.dist-info/METADATA,sha256=jmUv-NLDPXbUdEGIEy5HZ94y-UfQzpFziq6s4bj7PEE,15632
274
+ flwr_nightly-1.10.0.dev20240713.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
275
+ flwr_nightly-1.10.0.dev20240713.dist-info/entry_points.txt,sha256=7qBQcA-bDGDxnJmLd9FYqglFQubjCNqyg9M8a-lukps,336
276
+ flwr_nightly-1.10.0.dev20240713.dist-info/RECORD,,