flwr-nightly 1.11.0.dev20240828__py3-none-any.whl → 1.11.1.dev20240911__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 (27) hide show
  1. flwr/cli/new/new.py +23 -37
  2. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +19 -29
  3. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -3
  4. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +18 -3
  5. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +16 -13
  6. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +9 -1
  7. flwr/client/__init__.py +0 -4
  8. flwr/client/client_app.py +2 -2
  9. flwr/client/grpc_rere_client/client_interceptor.py +15 -7
  10. flwr/common/record/recordset.py +1 -1
  11. flwr/common/record/typeddict.py +24 -1
  12. flwr/common/telemetry.py +36 -30
  13. flwr/server/__init__.py +0 -4
  14. flwr/server/compat/app.py +0 -5
  15. flwr/server/driver/grpc_driver.py +1 -3
  16. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +2 -1
  17. flwr/server/superlink/fleet/vce/backend/raybackend.py +21 -12
  18. flwr/simulation/ray_transport/ray_actor.py +2 -2
  19. flwr/simulation/run_simulation.py +37 -8
  20. flwr/superexec/__init__.py +0 -6
  21. flwr/superexec/app.py +3 -1
  22. {flwr_nightly-1.11.0.dev20240828.dist-info → flwr_nightly-1.11.1.dev20240911.dist-info}/METADATA +1 -1
  23. {flwr_nightly-1.11.0.dev20240828.dist-info → flwr_nightly-1.11.1.dev20240911.dist-info}/RECORD +26 -26
  24. flwr_nightly-1.11.1.dev20240911.dist-info/entry_points.txt +10 -0
  25. flwr_nightly-1.11.0.dev20240828.dist-info/entry_points.txt +0 -10
  26. {flwr_nightly-1.11.0.dev20240828.dist-info → flwr_nightly-1.11.1.dev20240911.dist-info}/LICENSE +0 -0
  27. {flwr_nightly-1.11.0.dev20240828.dist-info → flwr_nightly-1.11.1.dev20240911.dist-info}/WHEEL +0 -0
flwr/cli/new/new.py CHANGED
@@ -136,36 +136,23 @@ def new(
136
136
  username = prompt_text("Please provide your Flower username")
137
137
 
138
138
  if framework is not None:
139
- framework_str_upper = str(framework.value)
139
+ framework_str = str(framework.value)
140
140
  else:
141
- framework_value = prompt_options(
141
+ framework_str = prompt_options(
142
142
  "Please select ML framework by typing in the number",
143
143
  [mlf.value for mlf in MlFramework],
144
144
  )
145
- selected_value = [
146
- name
147
- for name, value in vars(MlFramework).items()
148
- if value == framework_value
149
- ]
150
- framework_str_upper = selected_value[0]
151
-
152
- framework_str = framework_str_upper.lower()
153
145
 
154
146
  llm_challenge_str = None
155
- if framework_str == "flowertune":
147
+ if framework_str == MlFramework.FLOWERTUNE:
156
148
  llm_challenge_value = prompt_options(
157
149
  "Please select LLM challenge by typing in the number",
158
150
  sorted([challenge.value for challenge in LlmChallengeName]),
159
151
  )
160
- selected_value = [
161
- name
162
- for name, value in vars(LlmChallengeName).items()
163
- if value == llm_challenge_value
164
- ]
165
- llm_challenge_str = selected_value[0]
166
- llm_challenge_str = llm_challenge_str.lower()
152
+ llm_challenge_str = llm_challenge_value.lower()
167
153
 
168
- is_baseline_project = framework_str == "baseline"
154
+ if framework_str == MlFramework.BASELINE:
155
+ framework_str = "baseline"
169
156
 
170
157
  print(
171
158
  typer.style(
@@ -176,19 +163,21 @@ def new(
176
163
  )
177
164
 
178
165
  context = {
179
- "framework_str": framework_str_upper,
166
+ "framework_str": framework_str,
180
167
  "import_name": import_name.replace("-", "_"),
181
168
  "package_name": package_name,
182
169
  "project_name": app_name,
183
170
  "username": username,
184
171
  }
185
172
 
173
+ template_name = framework_str.lower()
174
+
186
175
  # List of files to render
187
176
  if llm_challenge_str:
188
177
  files = {
189
178
  ".gitignore": {"template": "app/.gitignore.tpl"},
190
- "pyproject.toml": {"template": f"app/pyproject.{framework_str}.toml.tpl"},
191
- "README.md": {"template": f"app/README.{framework_str}.md.tpl"},
179
+ "pyproject.toml": {"template": f"app/pyproject.{template_name}.toml.tpl"},
180
+ "README.md": {"template": f"app/README.{template_name}.md.tpl"},
192
181
  f"{import_name}/__init__.py": {"template": "app/code/__init__.py.tpl"},
193
182
  f"{import_name}/server_app.py": {
194
183
  "template": "app/code/flwr_tune/server_app.py.tpl"
@@ -196,7 +185,6 @@ def new(
196
185
  f"{import_name}/client_app.py": {
197
186
  "template": "app/code/flwr_tune/client_app.py.tpl"
198
187
  },
199
- f"{import_name}/app.py": {"template": "app/code/flwr_tune/app.py.tpl"},
200
188
  f"{import_name}/models.py": {
201
189
  "template": "app/code/flwr_tune/models.py.tpl"
202
190
  },
@@ -236,44 +224,42 @@ def new(
236
224
  files = {
237
225
  ".gitignore": {"template": "app/.gitignore.tpl"},
238
226
  "README.md": {"template": "app/README.md.tpl"},
239
- "pyproject.toml": {"template": f"app/pyproject.{framework_str}.toml.tpl"},
227
+ "pyproject.toml": {"template": f"app/pyproject.{template_name}.toml.tpl"},
240
228
  f"{import_name}/__init__.py": {"template": "app/code/__init__.py.tpl"},
241
229
  f"{import_name}/server_app.py": {
242
- "template": f"app/code/server.{framework_str}.py.tpl"
230
+ "template": f"app/code/server.{template_name}.py.tpl"
243
231
  },
244
232
  f"{import_name}/client_app.py": {
245
- "template": f"app/code/client.{framework_str}.py.tpl"
233
+ "template": f"app/code/client.{template_name}.py.tpl"
246
234
  },
247
235
  }
248
236
 
249
237
  # Depending on the framework, generate task.py file
250
238
  frameworks_with_tasks = [
251
- MlFramework.PYTORCH.value.lower(),
252
- MlFramework.JAX.value.lower(),
253
- MlFramework.HUGGINGFACE.value.lower(),
254
- MlFramework.MLX.value.lower(),
255
- MlFramework.TENSORFLOW.value.lower(),
239
+ MlFramework.PYTORCH.value,
240
+ MlFramework.JAX.value,
241
+ MlFramework.HUGGINGFACE.value,
242
+ MlFramework.MLX.value,
243
+ MlFramework.TENSORFLOW.value,
256
244
  ]
257
245
  if framework_str in frameworks_with_tasks:
258
246
  files[f"{import_name}/task.py"] = {
259
- "template": f"app/code/task.{framework_str}.py.tpl"
247
+ "template": f"app/code/task.{template_name}.py.tpl"
260
248
  }
261
249
 
262
- if is_baseline_project:
250
+ if framework_str == "baseline":
263
251
  # Include additional files for baseline template
264
252
  for file_name in ["model", "dataset", "strategy", "utils", "__init__"]:
265
253
  files[f"{import_name}/{file_name}.py"] = {
266
- "template": f"app/code/{file_name}.{framework_str}.py.tpl"
254
+ "template": f"app/code/{file_name}.{template_name}.py.tpl"
267
255
  }
268
256
 
269
257
  # Replace README.md
270
- files["README.md"]["template"] = f"app/README.{framework_str}.md.tpl"
258
+ files["README.md"]["template"] = f"app/README.{template_name}.md.tpl"
271
259
 
272
260
  # Add LICENSE
273
261
  files["LICENSE"] = {"template": "app/LICENSE.tpl"}
274
262
 
275
- context["framework_str"] = "baseline"
276
-
277
263
  for file_path, value in files.items():
278
264
  render_and_create(
279
265
  file_path=project_dir / file_path,
@@ -1,18 +1,11 @@
1
1
  """$project_name: A Flower / $framework_str app."""
2
2
 
3
+ import torch
3
4
  from flwr.client import ClientApp, NumPyClient
4
5
  from flwr.common import Context
5
6
  from transformers import AutoModelForSequenceClassification
6
7
 
7
- from $import_name.task import (
8
- get_weights,
9
- load_data,
10
- set_weights,
11
- train,
12
- test,
13
- CHECKPOINT,
14
- DEVICE,
15
- )
8
+ from $import_name.task import get_weights, load_data, set_weights, test, train
16
9
 
17
10
 
18
11
  # Flower client
@@ -22,37 +15,34 @@ class FlowerClient(NumPyClient):
22
15
  self.trainloader = trainloader
23
16
  self.testloader = testloader
24
17
  self.local_epochs = local_epochs
25
-
26
- def get_parameters(self, config):
27
- return get_weights(self.net)
28
-
29
- def set_parameters(self, parameters):
30
- set_weights(self.net, parameters)
18
+ self.device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
19
+ self.net.to(self.device)
31
20
 
32
21
  def fit(self, parameters, config):
33
- self.set_parameters(parameters)
34
- train(
35
- self.net,
36
- self.trainloader,
37
- epochs=self.local_epochs,
38
- )
39
- return self.get_parameters(config={}), len(self.trainloader), {}
22
+ set_weights(self.net, parameters)
23
+ train(self.net, self.trainloader, epochs=self.local_epochs, device=self.device)
24
+ return get_weights(self.net), len(self.trainloader), {}
40
25
 
41
26
  def evaluate(self, parameters, config):
42
- self.set_parameters(parameters)
43
- loss, accuracy = test(self.net, self.testloader)
27
+ set_weights(self.net, parameters)
28
+ loss, accuracy = test(self.net, self.testloader, self.device)
44
29
  return float(loss), len(self.testloader), {"accuracy": accuracy}
45
30
 
46
31
 
47
32
  def client_fn(context: Context):
48
- # Load model and data
49
- net = AutoModelForSequenceClassification.from_pretrained(
50
- CHECKPOINT, num_labels=2
51
- ).to(DEVICE)
52
33
 
34
+ # Get this client's dataset partition
53
35
  partition_id = context.node_config["partition-id"]
54
36
  num_partitions = context.node_config["num-partitions"]
55
- trainloader, valloader = load_data(partition_id, num_partitions)
37
+ model_name = context.run_config["model-name"]
38
+ trainloader, valloader = load_data(partition_id, num_partitions, model_name)
39
+
40
+ # Load model
41
+ num_labels = context.run_config["num-labels"]
42
+ net = AutoModelForSequenceClassification.from_pretrained(
43
+ model_name, num_labels=num_labels
44
+ )
45
+
56
46
  local_epochs = context.run_config["local-epochs"]
57
47
 
58
48
  # Return Client instance
@@ -17,9 +17,6 @@ class FlowerClient(NumPyClient):
17
17
  self.batch_size = batch_size
18
18
  self.verbose = verbose
19
19
 
20
- def get_parameters(self, config):
21
- return self.model.get_weights()
22
-
23
20
  def fit(self, parameters, config):
24
21
  self.model.set_weights(parameters)
25
22
  self.model.fit(
@@ -1,18 +1,33 @@
1
1
  """$project_name: A Flower / $framework_str app."""
2
2
 
3
- from flwr.common import Context
4
- from flwr.server.strategy import FedAvg
3
+ from flwr.common import Context, ndarrays_to_parameters
5
4
  from flwr.server import ServerApp, ServerAppComponents, ServerConfig
5
+ from flwr.server.strategy import FedAvg
6
+ from transformers import AutoModelForSequenceClassification
7
+
8
+ from $import_name.task import get_weights
6
9
 
7
10
 
8
11
  def server_fn(context: Context):
9
12
  # Read from config
10
13
  num_rounds = context.run_config["num-server-rounds"]
14
+ fraction_fit = context.run_config["fraction-fit"]
15
+
16
+ # Initialize global model
17
+ model_name = context.run_config["model-name"]
18
+ num_labels = context.run_config["num-labels"]
19
+ net = AutoModelForSequenceClassification.from_pretrained(
20
+ model_name, num_labels=num_labels
21
+ )
22
+
23
+ weights = get_weights(net)
24
+ initial_parameters = ndarrays_to_parameters(weights)
11
25
 
12
26
  # Define strategy
13
27
  strategy = FedAvg(
14
- fraction_fit=1.0,
28
+ fraction_fit=fraction_fit,
15
29
  fraction_evaluate=1.0,
30
+ initial_parameters=initial_parameters,
16
31
  )
17
32
  config = ServerConfig(num_rounds=num_rounds)
18
33
 
@@ -4,24 +4,25 @@ import warnings
4
4
  from collections import OrderedDict
5
5
 
6
6
  import torch
7
+ import transformers
8
+ from datasets.utils.logging import disable_progress_bar
7
9
  from evaluate import load as load_metric
10
+ from flwr_datasets import FederatedDataset
11
+ from flwr_datasets.partitioner import IidPartitioner
8
12
  from torch.optim import AdamW
9
13
  from torch.utils.data import DataLoader
10
14
  from transformers import AutoTokenizer, DataCollatorWithPadding
11
15
 
12
- from flwr_datasets import FederatedDataset
13
- from flwr_datasets.partitioner import IidPartitioner
14
-
15
-
16
16
  warnings.filterwarnings("ignore", category=UserWarning)
17
- DEVICE = torch.device("cpu")
18
- CHECKPOINT = "distilbert-base-uncased" # transformer model checkpoint
17
+ warnings.filterwarnings("ignore", category=FutureWarning)
18
+ disable_progress_bar()
19
+ transformers.logging.set_verbosity_error()
19
20
 
20
21
 
21
22
  fds = None # Cache FederatedDataset
22
23
 
23
24
 
24
- def load_data(partition_id: int, num_partitions: int):
25
+ def load_data(partition_id: int, num_partitions: int, model_name: str):
25
26
  """Load IMDB data (training and eval)"""
26
27
  # Only initialize `FederatedDataset` once
27
28
  global fds
@@ -35,10 +36,12 @@ def load_data(partition_id: int, num_partitions: int):
35
36
  # Divide data: 80% train, 20% test
36
37
  partition_train_test = partition.train_test_split(test_size=0.2, seed=42)
37
38
 
38
- tokenizer = AutoTokenizer.from_pretrained(CHECKPOINT)
39
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
39
40
 
40
41
  def tokenize_function(examples):
41
- return tokenizer(examples["text"], truncation=True)
42
+ return tokenizer(
43
+ examples["text"], truncation=True, add_special_tokens=True, max_length=512
44
+ )
42
45
 
43
46
  partition_train_test = partition_train_test.map(tokenize_function, batched=True)
44
47
  partition_train_test = partition_train_test.remove_columns("text")
@@ -59,12 +62,12 @@ def load_data(partition_id: int, num_partitions: int):
59
62
  return trainloader, testloader
60
63
 
61
64
 
62
- def train(net, trainloader, epochs):
65
+ def train(net, trainloader, epochs, device):
63
66
  optimizer = AdamW(net.parameters(), lr=5e-5)
64
67
  net.train()
65
68
  for _ in range(epochs):
66
69
  for batch in trainloader:
67
- batch = {k: v.to(DEVICE) for k, v in batch.items()}
70
+ batch = {k: v.to(device) for k, v in batch.items()}
68
71
  outputs = net(**batch)
69
72
  loss = outputs.loss
70
73
  loss.backward()
@@ -72,12 +75,12 @@ def train(net, trainloader, epochs):
72
75
  optimizer.zero_grad()
73
76
 
74
77
 
75
- def test(net, testloader):
78
+ def test(net, testloader, device):
76
79
  metric = load_metric("accuracy")
77
80
  loss = 0
78
81
  net.eval()
79
82
  for batch in testloader:
80
- batch = {k: v.to(DEVICE) for k, v in batch.items()}
83
+ batch = {k: v.to(device) for k, v in batch.items()}
81
84
  with torch.no_grad():
82
85
  outputs = net(**batch)
83
86
  logits = outputs.logits
@@ -8,7 +8,7 @@ version = "1.0.0"
8
8
  description = ""
9
9
  license = "Apache-2.0"
10
10
  dependencies = [
11
- "flwr[simulation]>=1.10.0",
11
+ "flwr[simulation]>=1.11.0",
12
12
  "flwr-datasets>=0.3.0",
13
13
  "torch==2.2.1",
14
14
  "transformers>=4.30.0,<5.0",
@@ -29,10 +29,18 @@ clientapp = "$import_name.client_app:app"
29
29
 
30
30
  [tool.flwr.app.config]
31
31
  num-server-rounds = 3
32
+ fraction-fit = 0.5
32
33
  local-epochs = 1
34
+ model-name = "prajjwal1/bert-tiny" # Set a larger model if you have access to more GPU resources
35
+ num-labels = 2
33
36
 
34
37
  [tool.flwr.federations]
35
38
  default = "localhost"
36
39
 
37
40
  [tool.flwr.federations.localhost]
38
41
  options.num-supernodes = 10
42
+
43
+ [tool.flwr.federations.localhost-gpu]
44
+ options.num-supernodes = 10
45
+ options.backend.client-resources.num-cpus = 4 # each ClientApp assumes to use 4CPUs
46
+ options.backend.client-resources.num-gpus = 0.25 # at most 4 ClientApps will run in a given GPU
flwr/client/__init__.py CHANGED
@@ -20,8 +20,6 @@ from .app import start_numpy_client as start_numpy_client
20
20
  from .client import Client as Client
21
21
  from .client_app import ClientApp as ClientApp
22
22
  from .numpy_client import NumPyClient as NumPyClient
23
- from .supernode import run_client_app as run_client_app
24
- from .supernode import run_supernode as run_supernode
25
23
  from .typing import ClientFn as ClientFn
26
24
  from .typing import ClientFnExt as ClientFnExt
27
25
 
@@ -32,8 +30,6 @@ __all__ = [
32
30
  "ClientFnExt",
33
31
  "NumPyClient",
34
32
  "mod",
35
- "run_client_app",
36
- "run_supernode",
37
33
  "start_client",
38
34
  "start_numpy_client",
39
35
  ]
flwr/client/client_app.py CHANGED
@@ -41,11 +41,11 @@ def _alert_erroneous_client_fn() -> None:
41
41
 
42
42
  def _inspect_maybe_adapt_client_fn_signature(client_fn: ClientFnExt) -> ClientFnExt:
43
43
  client_fn_args = inspect.signature(client_fn).parameters
44
- first_arg = list(client_fn_args.keys())[0]
45
44
 
46
45
  if len(client_fn_args) != 1:
47
46
  _alert_erroneous_client_fn()
48
47
 
48
+ first_arg = list(client_fn_args.keys())[0]
49
49
  first_arg_type = client_fn_args[first_arg].annotation
50
50
 
51
51
  if first_arg_type is str or first_arg == "cid":
@@ -263,7 +263,7 @@ def _registration_error(fn_name: str) -> ValueError:
263
263
  >>> class FlowerClient(NumPyClient):
264
264
  >>> # ...
265
265
  >>>
266
- >>> def client_fn(cid) -> Client:
266
+ >>> def client_fn(context: Context):
267
267
  >>> return FlowerClient().to_client()
268
268
  >>>
269
269
  >>> app = ClientApp(
@@ -17,11 +17,13 @@
17
17
 
18
18
  import base64
19
19
  import collections
20
+ from logging import WARNING
20
21
  from typing import Any, Callable, Optional, Sequence, Tuple, Union
21
22
 
22
23
  import grpc
23
24
  from cryptography.hazmat.primitives.asymmetric import ec
24
25
 
26
+ from flwr.common.logger import log
25
27
  from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
26
28
  bytes_to_public_key,
27
29
  compute_hmac,
@@ -128,13 +130,12 @@ class AuthenticateClientInterceptor(grpc.UnaryUnaryClientInterceptor): # type:
128
130
  if self.shared_secret is None:
129
131
  raise RuntimeError("Failure to compute hmac")
130
132
 
133
+ message_bytes = request.SerializeToString(deterministic=True)
131
134
  metadata.append(
132
135
  (
133
136
  _AUTH_TOKEN_HEADER,
134
137
  base64.urlsafe_b64encode(
135
- compute_hmac(
136
- self.shared_secret, request.SerializeToString(True)
137
- )
138
+ compute_hmac(self.shared_secret, message_bytes)
138
139
  ),
139
140
  )
140
141
  )
@@ -151,8 +152,15 @@ class AuthenticateClientInterceptor(grpc.UnaryUnaryClientInterceptor): # type:
151
152
  server_public_key_bytes = base64.urlsafe_b64decode(
152
153
  _get_value_from_tuples(_PUBLIC_KEY_HEADER, response.initial_metadata())
153
154
  )
154
- self.server_public_key = bytes_to_public_key(server_public_key_bytes)
155
- self.shared_secret = generate_shared_key(
156
- self.private_key, self.server_public_key
157
- )
155
+
156
+ if server_public_key_bytes != b"":
157
+ self.server_public_key = bytes_to_public_key(server_public_key_bytes)
158
+ else:
159
+ log(WARNING, "Can't get server public key, SuperLink may be offline")
160
+
161
+ if self.server_public_key is not None:
162
+ self.shared_secret = generate_shared_key(
163
+ self.private_key, self.server_public_key
164
+ )
165
+
158
166
  return response
@@ -119,7 +119,7 @@ class RecordSet:
119
119
  Let's see an example.
120
120
 
121
121
  >>> from flwr.common import RecordSet
122
- >>> from flwr.common import ConfigsRecords, MetricsRecords, ParametersRecord
122
+ >>> from flwr.common import ConfigsRecord, MetricsRecord, ParametersRecord
123
123
  >>>
124
124
  >>> # Let's begin with an empty record
125
125
  >>> my_recordset = RecordSet()
@@ -15,7 +15,18 @@
15
15
  """Typed dict base class for *Records."""
16
16
 
17
17
 
18
- from typing import Callable, Dict, Generic, Iterator, MutableMapping, TypeVar, cast
18
+ from typing import (
19
+ Callable,
20
+ Dict,
21
+ Generic,
22
+ ItemsView,
23
+ Iterator,
24
+ KeysView,
25
+ MutableMapping,
26
+ TypeVar,
27
+ ValuesView,
28
+ cast,
29
+ )
19
30
 
20
31
  K = TypeVar("K") # Key type
21
32
  V = TypeVar("V") # Value type
@@ -73,3 +84,15 @@ class TypedDict(MutableMapping[K, V], Generic[K, V]):
73
84
  if isinstance(other, dict):
74
85
  return data == other
75
86
  return NotImplemented
87
+
88
+ def keys(self) -> KeysView[K]:
89
+ """D.keys() -> a set-like object providing a view on D's keys."""
90
+ return cast(Dict[K, V], self.__dict__["_data"]).keys()
91
+
92
+ def values(self) -> ValuesView[V]:
93
+ """D.values() -> an object providing a view on D's values."""
94
+ return cast(Dict[K, V], self.__dict__["_data"]).values()
95
+
96
+ def items(self) -> ItemsView[K, V]:
97
+ """D.items() -> a set-like object providing a view on D's items."""
98
+ return cast(Dict[K, V], self.__dict__["_data"]).items()
flwr/common/telemetry.py CHANGED
@@ -132,53 +132,59 @@ class EventType(str, Enum):
132
132
  # Ping
133
133
  PING = auto()
134
134
 
135
- # Client: start_client
135
+ # --- LEGACY FUNCTIONS -------------------------------------------------------------
136
+
137
+ # Legacy: `start_client` function
136
138
  START_CLIENT_ENTER = auto()
137
139
  START_CLIENT_LEAVE = auto()
138
140
 
139
- # Server: start_server
141
+ # Legacy: `start_server` function
140
142
  START_SERVER_ENTER = auto()
141
143
  START_SERVER_LEAVE = auto()
142
144
 
143
- # Driver API
144
- RUN_DRIVER_API_ENTER = auto()
145
- RUN_DRIVER_API_LEAVE = auto()
145
+ # Legacy: `start_simulation` function
146
+ START_SIMULATION_ENTER = auto()
147
+ START_SIMULATION_LEAVE = auto()
146
148
 
147
- # Fleet API
148
- RUN_FLEET_API_ENTER = auto()
149
- RUN_FLEET_API_LEAVE = auto()
149
+ # --- `flwr` CLI -------------------------------------------------------------------
150
150
 
151
- # Driver API and Fleet API
152
- RUN_SUPERLINK_ENTER = auto()
153
- RUN_SUPERLINK_LEAVE = auto()
151
+ # Not yet implemented
154
152
 
155
- # Simulation
156
- START_SIMULATION_ENTER = auto()
157
- START_SIMULATION_LEAVE = auto()
153
+ # --- SuperExec --------------------------------------------------------------------
158
154
 
159
- # Driver: Driver
160
- DRIVER_CONNECT = auto()
161
- DRIVER_DISCONNECT = auto()
155
+ # SuperExec
156
+ RUN_SUPEREXEC_ENTER = auto()
157
+ RUN_SUPEREXEC_LEAVE = auto()
162
158
 
163
- # Driver: start_driver
164
- START_DRIVER_ENTER = auto()
165
- START_DRIVER_LEAVE = auto()
159
+ # --- Simulation Engine ------------------------------------------------------------
166
160
 
167
- # flower-client-app
168
- RUN_CLIENT_APP_ENTER = auto()
169
- RUN_CLIENT_APP_LEAVE = auto()
161
+ # CLI: flower-simulation
162
+ CLI_FLOWER_SIMULATION_ENTER = auto()
163
+ CLI_FLOWER_SIMULATION_LEAVE = auto()
170
164
 
171
- # flower-server-app
172
- RUN_SERVER_APP_ENTER = auto()
173
- RUN_SERVER_APP_LEAVE = auto()
165
+ # Python API: `run_simulation`
166
+ PYTHON_API_RUN_SIMULATION_ENTER = auto()
167
+ PYTHON_API_RUN_SIMULATION_LEAVE = auto()
174
168
 
175
- # SuperNode
169
+ # --- Deployment Engine ------------------------------------------------------------
170
+
171
+ # CLI: `flower-superlink`
172
+ RUN_SUPERLINK_ENTER = auto()
173
+ RUN_SUPERLINK_LEAVE = auto()
174
+
175
+ # CLI: `flower-supernode`
176
176
  RUN_SUPERNODE_ENTER = auto()
177
177
  RUN_SUPERNODE_LEAVE = auto()
178
178
 
179
- # SuperExec
180
- RUN_SUPEREXEC_ENTER = auto()
181
- RUN_SUPEREXEC_LEAVE = auto()
179
+ # CLI: `flower-server-app`
180
+ RUN_SERVER_APP_ENTER = auto()
181
+ RUN_SERVER_APP_LEAVE = auto()
182
+
183
+ # --- DEPRECATED -------------------------------------------------------------------
184
+
185
+ # [DEPRECATED] CLI: `flower-client-app`
186
+ RUN_CLIENT_APP_ENTER = auto()
187
+ RUN_CLIENT_APP_LEAVE = auto()
182
188
 
183
189
 
184
190
  # Use the ThreadPoolExecutor with max_workers=1 to have a queue
flwr/server/__init__.py CHANGED
@@ -17,14 +17,12 @@
17
17
 
18
18
  from . import strategy
19
19
  from . import workflow as workflow
20
- from .app import run_superlink as run_superlink
21
20
  from .app import start_server as start_server
22
21
  from .client_manager import ClientManager as ClientManager
23
22
  from .client_manager import SimpleClientManager as SimpleClientManager
24
23
  from .compat import LegacyContext as LegacyContext
25
24
  from .driver import Driver as Driver
26
25
  from .history import History as History
27
- from .run_serverapp import run_server_app as run_server_app
28
26
  from .server import Server as Server
29
27
  from .server_app import ServerApp as ServerApp
30
28
  from .server_config import ServerConfig as ServerConfig
@@ -40,8 +38,6 @@ __all__ = [
40
38
  "ServerAppComponents",
41
39
  "ServerConfig",
42
40
  "SimpleClientManager",
43
- "run_server_app",
44
- "run_superlink",
45
41
  "start_server",
46
42
  "strategy",
47
43
  "workflow",
flwr/server/compat/app.py CHANGED
@@ -18,7 +18,6 @@
18
18
  from logging import INFO
19
19
  from typing import Optional
20
20
 
21
- from flwr.common import EventType, event
22
21
  from flwr.common.logger import log
23
22
  from flwr.server.client_manager import ClientManager
24
23
  from flwr.server.history import History
@@ -65,8 +64,6 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
65
64
  hist : flwr.server.history.History
66
65
  Object containing training and evaluation metrics.
67
66
  """
68
- event(EventType.START_DRIVER_ENTER)
69
-
70
67
  # Initialize the Driver API server and config
71
68
  initialized_server, initialized_config = init_defaults(
72
69
  server=server,
@@ -96,6 +93,4 @@ def start_driver( # pylint: disable=too-many-arguments, too-many-locals
96
93
  f_stop.set()
97
94
  thread.join()
98
95
 
99
- event(EventType.START_SERVER_LEAVE)
100
-
101
96
  return hist
@@ -21,7 +21,7 @@ from typing import Iterable, List, Optional, cast
21
21
 
22
22
  import grpc
23
23
 
24
- from flwr.common import DEFAULT_TTL, EventType, Message, Metadata, RecordSet, event
24
+ from flwr.common import DEFAULT_TTL, Message, Metadata, RecordSet
25
25
  from flwr.common.grpc import create_channel
26
26
  from flwr.common.logger import log
27
27
  from flwr.common.serde import (
@@ -94,7 +94,6 @@ class GrpcDriver(Driver):
94
94
 
95
95
  This will not call GetRun.
96
96
  """
97
- event(EventType.DRIVER_CONNECT)
98
97
  if self._is_connected:
99
98
  log(WARNING, "Already connected")
100
99
  return
@@ -108,7 +107,6 @@ class GrpcDriver(Driver):
108
107
 
109
108
  def _disconnect(self) -> None:
110
109
  """Disconnect from the Driver API."""
111
- event(EventType.DRIVER_DISCONNECT)
112
110
  if not self._is_connected:
113
111
  log(DEBUG, "Already disconnected")
114
112
  return
@@ -188,7 +188,8 @@ class AuthenticateServerInterceptor(grpc.ServerInterceptor): # type: ignore
188
188
  self, public_key: ec.EllipticCurvePublicKey, request: Request, hmac_value: bytes
189
189
  ) -> bool:
190
190
  shared_secret = generate_shared_key(self.server_private_key, public_key)
191
- return verify_hmac(shared_secret, request.SerializeToString(True), hmac_value)
191
+ message_bytes = request.SerializeToString(deterministic=True)
192
+ return verify_hmac(shared_secret, message_bytes, hmac_value)
192
193
 
193
194
  def _create_authenticated_node(
194
195
  self,
@@ -52,16 +52,11 @@ class RayBackend(Backend):
52
52
 
53
53
  # Validate client resources
54
54
  self.client_resources_key = "client_resources"
55
- client_resources = self._validate_client_resources(config=backend_config)
55
+ self.client_resources = self._validate_client_resources(config=backend_config)
56
56
 
57
- # Create actor pool
58
- actor_kwargs = self._validate_actor_arguments(config=backend_config)
59
-
60
- self.pool = BasicActorPool(
61
- actor_type=ClientAppActor,
62
- client_resources=client_resources,
63
- actor_kwargs=actor_kwargs,
64
- )
57
+ # Valide actor resources
58
+ self.actor_kwargs = self._validate_actor_arguments(config=backend_config)
59
+ self.pool: Optional[BasicActorPool] = None
65
60
 
66
61
  self.app_fn: Optional[Callable[[], ClientApp]] = None
67
62
 
@@ -122,14 +117,24 @@ class RayBackend(Backend):
122
117
  @property
123
118
  def num_workers(self) -> int:
124
119
  """Return number of actors in pool."""
125
- return self.pool.num_actors
120
+ return self.pool.num_actors if self.pool else 0
126
121
 
127
122
  def is_worker_idle(self) -> bool:
128
123
  """Report whether the pool has idle actors."""
129
- return self.pool.is_actor_available()
124
+ return self.pool.is_actor_available() if self.pool else False
130
125
 
131
126
  def build(self, app_fn: Callable[[], ClientApp]) -> None:
132
127
  """Build pool of Ray actors that this backend will submit jobs to."""
128
+ # Create Actor Pool
129
+ try:
130
+ self.pool = BasicActorPool(
131
+ actor_type=ClientAppActor,
132
+ client_resources=self.client_resources,
133
+ actor_kwargs=self.actor_kwargs,
134
+ )
135
+ except Exception as ex:
136
+ raise ex
137
+
133
138
  self.pool.add_actors_to_pool(self.pool.actors_capacity)
134
139
  # Set ClientApp callable that ray actors will use
135
140
  self.app_fn = app_fn
@@ -146,6 +151,9 @@ class RayBackend(Backend):
146
151
  """
147
152
  partition_id = context.node_config[PARTITION_ID_KEY]
148
153
 
154
+ if self.pool is None:
155
+ raise ValueError("The actor pool is empty, unfit to process messages.")
156
+
149
157
  if self.app_fn is None:
150
158
  raise ValueError(
151
159
  "Unspecified function to load a `ClientApp`. "
@@ -179,6 +187,7 @@ class RayBackend(Backend):
179
187
 
180
188
  def terminate(self) -> None:
181
189
  """Terminate all actors in actor pool."""
182
- self.pool.terminate_all_actors()
190
+ if self.pool:
191
+ self.pool.terminate_all_actors()
183
192
  ray.shutdown()
184
193
  log(DEBUG, "Terminated %s", self.__class__.__name__)
@@ -124,14 +124,14 @@ def pool_size_from_resources(client_resources: Dict[str, Union[int, float]]) ->
124
124
  WARNING,
125
125
  "The ActorPool is empty. The system (CPUs=%s, GPUs=%s) "
126
126
  "does not meet the criteria to host at least one client with resources:"
127
- " %s. Lowering the `client_resources` could help.",
127
+ " %s. Lowering these resources could help.",
128
128
  num_cpus,
129
129
  num_gpus,
130
130
  client_resources,
131
131
  )
132
132
  raise ValueError(
133
133
  "ActorPool is empty. Stopping Simulation. "
134
- "Check 'client_resources' passed to `start_simulation`"
134
+ "Check `num_cpus` and/or `num_gpus` passed to the simulation engine"
135
135
  )
136
136
 
137
137
  return total_num_actors
@@ -109,6 +109,11 @@ def run_simulation_from_cli() -> None:
109
109
  """Run Simulation Engine from the CLI."""
110
110
  args = _parse_args_run_simulation().parse_args()
111
111
 
112
+ event(
113
+ EventType.CLI_FLOWER_SIMULATION_ENTER,
114
+ event_details={"backend": args.backend, "num-supernodes": args.num_supernodes},
115
+ )
116
+
112
117
  # Add warnings for deprecated server_app and client_app arguments
113
118
  if args.server_app:
114
119
  warn_deprecated_feature(
@@ -177,7 +182,9 @@ def run_simulation_from_cli() -> None:
177
182
  client_app_attr = app_components["clientapp"]
178
183
  server_app_attr = app_components["serverapp"]
179
184
 
180
- override_config = parse_config_args([args.run_config])
185
+ override_config = parse_config_args(
186
+ [args.run_config] if args.run_config else args.run_config
187
+ )
181
188
  fused_config = get_fused_config_from_dir(app_path, override_config)
182
189
  app_dir = args.app
183
190
  is_app = True
@@ -209,9 +216,11 @@ def run_simulation_from_cli() -> None:
209
216
  app_dir=app_dir,
210
217
  run=run,
211
218
  enable_tf_gpu_growth=args.enable_tf_gpu_growth,
219
+ delay_start=args.delay_start,
212
220
  verbose_logging=args.verbose,
213
221
  server_app_run_config=fused_config,
214
222
  is_app=is_app,
223
+ exit_event=EventType.CLI_FLOWER_SIMULATION_LEAVE,
215
224
  )
216
225
 
217
226
 
@@ -265,6 +274,11 @@ def run_simulation(
265
274
  When disabled, only INFO, WARNING and ERROR log messages will be shown. If
266
275
  enabled, DEBUG-level logs will be displayed.
267
276
  """
277
+ event(
278
+ EventType.PYTHON_API_RUN_SIMULATION_ENTER,
279
+ event_details={"backend": backend_name, "num-supernodes": num_supernodes},
280
+ )
281
+
268
282
  if enable_tf_gpu_growth:
269
283
  warn_deprecated_feature_with_example(
270
284
  "Passing `enable_tf_gpu_growth=True` is deprecated.",
@@ -282,6 +296,7 @@ def run_simulation(
282
296
  backend_config=backend_config,
283
297
  enable_tf_gpu_growth=enable_tf_gpu_growth,
284
298
  verbose_logging=verbose_logging,
299
+ exit_event=EventType.PYTHON_API_RUN_SIMULATION_LEAVE,
285
300
  )
286
301
 
287
302
 
@@ -295,7 +310,6 @@ def run_serverapp_th(
295
310
  f_stop: threading.Event,
296
311
  has_exception: threading.Event,
297
312
  enable_tf_gpu_growth: bool,
298
- delay_launch: int = 3,
299
313
  ) -> threading.Thread:
300
314
  """Run SeverApp in a thread."""
301
315
 
@@ -351,7 +365,6 @@ def run_serverapp_th(
351
365
  server_app,
352
366
  ),
353
367
  )
354
- sleep(delay_launch)
355
368
  serverapp_th.start()
356
369
  return serverapp_th
357
370
 
@@ -365,6 +378,8 @@ def _main_loop(
365
378
  is_app: bool,
366
379
  enable_tf_gpu_growth: bool,
367
380
  run: Run,
381
+ exit_event: EventType,
382
+ delay_start: int,
368
383
  flwr_dir: Optional[str] = None,
369
384
  client_app: Optional[ClientApp] = None,
370
385
  client_app_attr: Optional[str] = None,
@@ -372,7 +387,7 @@ def _main_loop(
372
387
  server_app_attr: Optional[str] = None,
373
388
  server_app_run_config: Optional[UserConfig] = None,
374
389
  ) -> None:
375
- """Launch SuperLink with Simulation Engine, then ServerApp on a separate thread."""
390
+ """Start ServerApp on a separate thread, then launch Simulation Engine."""
376
391
  # Initialize StateFactory
377
392
  state_factory = StateFactory(":flwr-in-memory-state:")
378
393
 
@@ -380,6 +395,7 @@ def _main_loop(
380
395
  # A Threading event to indicate if an exception was raised in the ServerApp thread
381
396
  server_app_thread_has_exception = threading.Event()
382
397
  serverapp_th = None
398
+ success = True
383
399
  try:
384
400
  # Register run
385
401
  log(DEBUG, "Pre-registering run with id %s", run.run_id)
@@ -403,8 +419,10 @@ def _main_loop(
403
419
  enable_tf_gpu_growth=enable_tf_gpu_growth,
404
420
  )
405
421
 
406
- # SuperLink with Simulation Engine
407
- event(EventType.RUN_SUPERLINK_ENTER)
422
+ # Buffer time so the `ServerApp` in separate thread is ready
423
+ log(DEBUG, "Buffer time delay: %ds", delay_start)
424
+ sleep(delay_start)
425
+ # Start Simulation Engine
408
426
  vce.start_vce(
409
427
  num_supernodes=num_supernodes,
410
428
  client_app_attr=client_app_attr,
@@ -422,13 +440,13 @@ def _main_loop(
422
440
  except Exception as ex:
423
441
  log(ERROR, "An exception occurred !! %s", ex)
424
442
  log(ERROR, traceback.format_exc())
443
+ success = False
425
444
  raise RuntimeError("An error was encountered. Ending simulation.") from ex
426
445
 
427
446
  finally:
428
447
  # Trigger stop event
429
448
  f_stop.set()
430
-
431
- event(EventType.RUN_SUPERLINK_LEAVE)
449
+ event(exit_event, event_details={"success": success})
432
450
  if serverapp_th:
433
451
  serverapp_th.join()
434
452
  if server_app_thread_has_exception.is_set():
@@ -440,6 +458,7 @@ def _main_loop(
440
458
  # pylint: disable=too-many-arguments,too-many-locals
441
459
  def _run_simulation(
442
460
  num_supernodes: int,
461
+ exit_event: EventType,
443
462
  client_app: Optional[ClientApp] = None,
444
463
  server_app: Optional[ServerApp] = None,
445
464
  backend_name: str = "ray",
@@ -451,6 +470,7 @@ def _run_simulation(
451
470
  flwr_dir: Optional[str] = None,
452
471
  run: Optional[Run] = None,
453
472
  enable_tf_gpu_growth: bool = False,
473
+ delay_start: int = 5,
454
474
  verbose_logging: bool = False,
455
475
  is_app: bool = False,
456
476
  ) -> None:
@@ -506,6 +526,8 @@ def _run_simulation(
506
526
  is_app,
507
527
  enable_tf_gpu_growth,
508
528
  run,
529
+ exit_event,
530
+ delay_start,
509
531
  flwr_dir,
510
532
  client_app,
511
533
  client_app_attr,
@@ -593,6 +615,13 @@ def _parse_args_run_simulation() -> argparse.ArgumentParser:
593
615
  "Read more about how `tf.config.experimental.set_memory_growth()` works in "
594
616
  "the TensorFlow documentation: https://www.tensorflow.org/api/stable.",
595
617
  )
618
+ parser.add_argument(
619
+ "--delay-start",
620
+ type=int,
621
+ default=3,
622
+ help="Buffer time (in seconds) to delay the start the simulation engine after "
623
+ "the `ServerApp`, which runs in a separate thread, has been launched.",
624
+ )
596
625
  parser.add_argument(
597
626
  "--verbose",
598
627
  action="store_true",
@@ -13,9 +13,3 @@
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
15
  """Flower SuperExec service."""
16
-
17
- from .app import run_superexec as run_superexec
18
-
19
- __all__ = [
20
- "run_superexec",
21
- ]
flwr/superexec/app.py CHANGED
@@ -56,7 +56,9 @@ def run_superexec() -> None:
56
56
  address=address,
57
57
  executor=_load_executor(args),
58
58
  certificates=certificates,
59
- config=parse_config_args([args.executor_config]),
59
+ config=parse_config_args(
60
+ [args.executor_config] if args.executor_config else args.executor_config
61
+ ),
60
62
  )
61
63
 
62
64
  grpc_servers = [superexec_server]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flwr-nightly
3
- Version: 1.11.0.dev20240828
3
+ Version: 1.11.1.dev20240911
4
4
  Summary: Flower: A Friendly Federated Learning Framework
5
5
  Home-page: https://flower.ai
6
6
  License: Apache-2.0
@@ -6,7 +6,7 @@ flwr/cli/config_utils.py,sha256=mDGXbcIxG14UpkUplILBYUkSk5M1LeTzZYDGNx-pFpU,7540
6
6
  flwr/cli/example.py,sha256=1bGDYll3BXQY2kRqSN-oICqS5n1b9m0g0RvXTopXHl4,2215
7
7
  flwr/cli/install.py,sha256=tUncrbZYRbC9QEcWSeTER16plPEoU-ERP0-nMgWiSPo,7094
8
8
  flwr/cli/new/__init__.py,sha256=cQzK1WH4JP2awef1t2UQ2xjl1agVEz9rwutV18SWV1k,789
9
- flwr/cli/new/new.py,sha256=RXNf8rYy5N9lgHgMR9dhFFm_MUGMPkAZf3ROMfaZRgc,10158
9
+ flwr/cli/new/new.py,sha256=9nBm6yjy3q4AHxT-wzV3n5cgO82F19xajfrdu3DIfyk,9605
10
10
  flwr/cli/new/templates/__init__.py,sha256=4luU8RL-CK8JJCstQ_ON809W9bNTkY1l9zSaPKBkgwY,725
11
11
  flwr/cli/new/templates/app/.gitignore.tpl,sha256=XixnHdyeMB2vwkGtGnwHqoWpH-9WChdyG0GXe57duhc,3078
12
12
  flwr/cli/new/templates/app/LICENSE.tpl,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
@@ -18,13 +18,13 @@ flwr/cli/new/templates/app/code/__init__.baseline.py.tpl,sha256=YkHAgppUeD2BnBoG
18
18
  flwr/cli/new/templates/app/code/__init__.py,sha256=EM6vfvgAILKPaPn7H1wMV1Wi01WyZCP_Eg6NxD6oWg8,736
19
19
  flwr/cli/new/templates/app/code/__init__.py.tpl,sha256=J0Gn74E7khpLyKJVNqOPu7ev93vkcu1PZugsbxtABMw,52
20
20
  flwr/cli/new/templates/app/code/client.baseline.py.tpl,sha256=1htktXX3jXX05r0vuG_afjS1sXGtuONW9EpiQ7vSBes,1901
21
- flwr/cli/new/templates/app/code/client.huggingface.py.tpl,sha256=62wtB4k1yrDApiG-rvGlOYFuiwAVk8kqJFmyY_v8HLo,1803
21
+ flwr/cli/new/templates/app/code/client.huggingface.py.tpl,sha256=ifD08KwjdoGieV26hFCgf3PQB6rMhj_NZLo5iUUndm8,1846
22
22
  flwr/cli/new/templates/app/code/client.jax.py.tpl,sha256=c2LDew2V8BUybZJiz1FeB3Kq4ey0Q2s0S5qNPUTNmI4,1490
23
23
  flwr/cli/new/templates/app/code/client.mlx.py.tpl,sha256=gxipt57ldc741qwRqSWtsLQH05JODKdGMTtvoXiBzDA,2906
24
24
  flwr/cli/new/templates/app/code/client.numpy.py.tpl,sha256=DMUXvQd2dr-wEn0ZrYJQhZ0OFUT4PKoHXtiD2haWnCI,570
25
25
  flwr/cli/new/templates/app/code/client.pytorch.py.tpl,sha256=WczaR5avJUhfw2Grn2KEC4tDJ4voIYG-2pAy-7i2cT8,1685
26
26
  flwr/cli/new/templates/app/code/client.sklearn.py.tpl,sha256=xW9cuKhybk5S8IeDZhbeb0DNegDIJGEYrzMKsxgc2GE,2978
27
- flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=u3KKf7hC9xGqOIUJXYCHJ_jiIu3aVbsC8pxVxm4yN6I,1759
27
+ flwr/cli/new/templates/app/code/client.tensorflow.py.tpl,sha256=yBiiU7B9Kf70U52cPkNs_dUpYrrTwbUi2os-PAyheaM,1680
28
28
  flwr/cli/new/templates/app/code/dataset.baseline.py.tpl,sha256=jbd_exHAk2-Blu_kVutjPO6a_dkJQWb232zxSeXIZ1k,1453
29
29
  flwr/cli/new/templates/app/code/flwr_tune/__init__.py,sha256=JgNgBtKdm1jKM9625WxappCAVUGtYAmcjKSsXJ1u3ZQ,748
30
30
  flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl,sha256=DbotzaXzLDwplVBkJLOe5Lt5b6Yutwv9rJ69oVwyrvU,4397
@@ -34,7 +34,7 @@ flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl,sha256=yMVcbfGkTPV9A
34
34
  flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl,sha256=BhiqRg9w1MGuU5h2_vrLhRc0oHItYzE69qX_JI411k8,2754
35
35
  flwr/cli/new/templates/app/code/model.baseline.py.tpl,sha256=cSz6-IWsnMl7s04DW4URINiIppCIberrtE8NqK6Qz48,2571
36
36
  flwr/cli/new/templates/app/code/server.baseline.py.tpl,sha256=outx7lDXsWS8QXKWOGOiDno6eE8WL7LBD51ZkAuC3WU,1570
37
- flwr/cli/new/templates/app/code/server.huggingface.py.tpl,sha256=etpjLvGu6pVXzYQBKZp4tTbD3zm461qFo24NliKo74U,591
37
+ flwr/cli/new/templates/app/code/server.huggingface.py.tpl,sha256=0PJmnZvR9_VPLSak1yVfkOx3dmqo6cynhY1l2s4AZrE,1158
38
38
  flwr/cli/new/templates/app/code/server.jax.py.tpl,sha256=pIdUH-LgWRAGWQYLlivMNf8XnDSNDe2cCuRjlxbRzys,529
39
39
  flwr/cli/new/templates/app/code/server.mlx.py.tpl,sha256=RqiZ0k468SOlm9dcPr-fvA8xcWv4zwDCbJfBwL7P9Us,529
40
40
  flwr/cli/new/templates/app/code/server.numpy.py.tpl,sha256=RqiZ0k468SOlm9dcPr-fvA8xcWv4zwDCbJfBwL7P9Us,529
@@ -42,7 +42,7 @@ flwr/cli/new/templates/app/code/server.pytorch.py.tpl,sha256=DW5c8vzXCvFeIE8YIWB
42
42
  flwr/cli/new/templates/app/code/server.sklearn.py.tpl,sha256=25Ae3kDqjDdBl8LwkDwye69nevd02Pk_e7F3SQKLdyk,624
43
43
  flwr/cli/new/templates/app/code/server.tensorflow.py.tpl,sha256=xMhQ7AumowgLkgUilgjVK7IbpRhPjslhVJU-vID6NY8,856
44
44
  flwr/cli/new/templates/app/code/strategy.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
45
- flwr/cli/new/templates/app/code/task.huggingface.py.tpl,sha256=G_LOeGErw-6WZAyuT01mXqR6s_BUrQYErXf_nHLujo4,3153
45
+ flwr/cli/new/templates/app/code/task.huggingface.py.tpl,sha256=ua6cAhJYPUCwML20DEucM0F4ZzzsEVQLYrRvhQ7CGRE,3347
46
46
  flwr/cli/new/templates/app/code/task.jax.py.tpl,sha256=F05eg149c9icRyVNdfcLyZvAXROQ7QhfifoGw_U1dsg,1530
47
47
  flwr/cli/new/templates/app/code/task.mlx.py.tpl,sha256=jWtCULLRr_9bCIJvoTLMx037-SDl_LF8udtA1UGoXDk,2946
48
48
  flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=NgbPix74X1t3ybaGjqdls30vF1i5oY3L7EQExhWhN74,3812
@@ -50,7 +50,7 @@ flwr/cli/new/templates/app/code/task.tensorflow.py.tpl,sha256=SKXAZdgBnPpbAbJ90R
50
50
  flwr/cli/new/templates/app/code/utils.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
51
51
  flwr/cli/new/templates/app/pyproject.baseline.toml.tpl,sha256=4gi90W9_B1kj6rYkpvVJxhNX9Yctsv9OH6CzXP-dcE4,2666
52
52
  flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=pogRZLrwSfN_XH4NxDdMkhMh1O_7DP90VOoP-cP0HvI,1827
53
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl,sha256=nD0rRUyr_Cj0TaSH8PsiaMhCwu_BuOVX4oqWfFSvOcE,765
53
+ flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl,sha256=CHJgkPNkJfzJhEbTe15uiV3AhOtIddQi-yofPZsCk3E,1143
54
54
  flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=Tq6jeGcoOKzMwWWYxMVnzMcipLURHLiW69iYlD1ywMg,659
55
55
  flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=SHwYAA2qgIlOAU3Sb9BKSZcZ7O9biACg27MHexXUtDw,741
56
56
  flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=-FCi64ygMgQke3zApUt0XtkIBo3WtQoPAPhtp_FqkPE,612
@@ -60,10 +60,10 @@ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=bRIvPCPvTTI4Eo5b
60
60
  flwr/cli/run/__init__.py,sha256=oCd6HmQDx-sqver1gecgx-uMA38BLTSiiKpl7RGNceg,789
61
61
  flwr/cli/run/run.py,sha256=RI6MgLBNYxmacjQg8XMAQ7VKxbV0DkRyJTfe4GsDFuw,7979
62
62
  flwr/cli/utils.py,sha256=l65Ul0YsSBPuypk0uorAtEDmLEYiUrzpCXi6zCg9mJ4,4506
63
- flwr/client/__init__.py,sha256=wzJZsYJIHf_8-PMzvfbinyzzjgh1UP1vLrAw2_yEbKI,1345
63
+ flwr/client/__init__.py,sha256=DGDoO0AEAfz-0CUFmLdyUUweAS64-07AOnmDfWUefK4,1192
64
64
  flwr/client/app.py,sha256=o_2bhmlBeZATtWnAPZhL-Q1Ly0QZxc9ou4i7t0HKumE,31956
65
65
  flwr/client/client.py,sha256=gy6WVlMUFAp8oevN4xpQPX30vPOIYGVqdbuFlTWkyG4,9080
66
- flwr/client/client_app.py,sha256=WcO4r6wrdfaus__3s22D2sYjfcptdgmVujUAYdNE6HU,10393
66
+ flwr/client/client_app.py,sha256=6TIH-B5lgKn199uzcjxfFKiGvKN5tmKYFpsovUrSXoo,10396
67
67
  flwr/client/clientapp/__init__.py,sha256=kZqChGnTChQ1WGSUkIlW2S5bc0d0mzDubCAmZUGRpEY,800
68
68
  flwr/client/clientapp/app.py,sha256=4QtblvJsZ0-V-QBbzyNqWV13ugrgJmkZnsHpBCuqgi8,7797
69
69
  flwr/client/clientapp/clientappio_servicer.py,sha256=5L6bjw_j3Mnx9kRFwYwxDNABKurBO5q1jZOWE_X11wQ,8522
@@ -74,7 +74,7 @@ flwr/client/grpc_adapter_client/connection.py,sha256=aOlCYasl8f2CrfcN-WrVEmvjAZF
74
74
  flwr/client/grpc_client/__init__.py,sha256=LsnbqXiJhgQcB0XzAlUQgPx011Uf7Y7yabIC1HxivJ8,735
75
75
  flwr/client/grpc_client/connection.py,sha256=czhRm23fwTgjN24Vf5nyNQ3hcb5Fo_5k-o9yZnelQCs,9362
76
76
  flwr/client/grpc_rere_client/__init__.py,sha256=MK-oSoV3kwUEQnIwl0GN4OpiHR7eLOrMA8ikunET130,752
77
- flwr/client/grpc_rere_client/client_interceptor.py,sha256=sYPEznuQPdy2BPDlvM9FK0ZRRucb4NfwUee1Z_mN82E,4954
77
+ flwr/client/grpc_rere_client/client_interceptor.py,sha256=eONCq-uRYvRCxjdP4wIi_FDynDipHs3DYjExgzAcJ5M,5240
78
78
  flwr/client/grpc_rere_client/connection.py,sha256=aiIWW9fVgJZNeZ9SjUAx5ax-3825JrjYc5E5l7XvzyM,10913
79
79
  flwr/client/grpc_rere_client/grpc_adapter.py,sha256=Pw7Toi4wCUIEdBMyv4yKirjgW6814gFhhAmsTYmV4IM,5005
80
80
  flwr/client/heartbeat.py,sha256=cx37mJBH8LyoIN4Lks85wtqT1mnU5GulQnr4pGCvAq0,2404
@@ -118,8 +118,8 @@ flwr/common/record/configsrecord.py,sha256=6nIpGaeB_o8SvjMfD_iehJC1efAHgWyaeGfLD
118
118
  flwr/common/record/conversion_utils.py,sha256=n3I3SI2P6hUjyxbWNc0QAch-SEhfMK6Hm-UUaplAlUc,1393
119
119
  flwr/common/record/metricsrecord.py,sha256=aTuw0gWjRaZhekf76zg0h0rES3iEnTIGPZ7-jN2PYO4,5764
120
120
  flwr/common/record/parametersrecord.py,sha256=mOcERCUylgWLQ-hISYmOHXlt-Qohv8YtPnJdLO0Kr8U,7730
121
- flwr/common/record/recordset.py,sha256=6-a6EYN2YlH0H62jymJcHGVFBB8gJo69U2lvWyAv60A,8314
122
- flwr/common/record/typeddict.py,sha256=M9zU1vjuPmKRxA0uiP2v752TB3D2VfrJzKNWx-EFviw,3016
121
+ flwr/common/record/recordset.py,sha256=sSofrBycZSqiHR4TzfI4_QoIIN-5B1LnMG0C9CiByAo,8312
122
+ flwr/common/record/typeddict.py,sha256=TDPLdeuHiAUy9JyfJN0VF1zSyqdnf_uNWUFjAd2z_Os,3623
123
123
  flwr/common/recordset_compat.py,sha256=5prvfx8mT4ZWIEU4Gmz2wzCbO4yPjQ3ONr20uKt3UM0,13973
124
124
  flwr/common/retry_invoker.py,sha256=dQY5fPIKhy9OiFswZhLxA9fB455u-DYCvDVcFJmrPDk,11707
125
125
  flwr/common/secure_aggregation/__init__.py,sha256=erPnTWdOfMH0K0HQTmj5foDJ6t3iYcExy2aACy8iZNQ,731
@@ -131,7 +131,7 @@ flwr/common/secure_aggregation/quantization.py,sha256=1obYr9qneaI8r-A0F_pghrPNG9
131
131
  flwr/common/secure_aggregation/secaggplus_constants.py,sha256=9MF-oQh62uD7rt9VeNB-rHf2gBLd5GL3S9OejCxmILY,2183
132
132
  flwr/common/secure_aggregation/secaggplus_utils.py,sha256=3VssKgYF7HQIkSpROnEUoYWVt47p12PE_Rj4nYqqg04,3221
133
133
  flwr/common/serde.py,sha256=SIKZa-TNCSWVtqx8L3aUkylN6gxK-IwXlgoukiYwYyc,29209
134
- flwr/common/telemetry.py,sha256=nSjJHDitPhzB2qUl6LeSMT9Zld5lIk9uW98RpxQwiZw,8366
134
+ flwr/common/telemetry.py,sha256=CZeBHqaH_W-H7yo-9LE-gV7ju5gJTXZztw4tJlFZqz8,8916
135
135
  flwr/common/typing.py,sha256=rGabiSkjFvGIHwmhDqtuu-LBvz7LVSj1vyMlNtA7VA0,5004
136
136
  flwr/common/version.py,sha256=W1ntylR04xkCP6zeSet6sRtBn7P1cje2lOqBJgYBjJY,1349
137
137
  flwr/proto/__init__.py,sha256=hbY7JYakwZwCkYgCNlmHdc8rtvfoJbAZLalMdc--CGc,683
@@ -192,19 +192,19 @@ flwr/proto/transport_pb2.pyi,sha256=CZvJRWTU3QWFWLXNFtyLSrSKFatIyMcy-ohzLbQ-G9c,
192
192
  flwr/proto/transport_pb2_grpc.py,sha256=vLN3EHtx2aEEMCO4f1Upu-l27BPzd3-5pV-u8wPcosk,2598
193
193
  flwr/proto/transport_pb2_grpc.pyi,sha256=AGXf8RiIiW2J5IKMlm_3qT3AzcDa4F3P5IqUjve_esA,766
194
194
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
195
- flwr/server/__init__.py,sha256=BxzPhvouvWFGi7CFpI5b4EeVR9XDqbK7Ndqg24EL_Rw,1679
195
+ flwr/server/__init__.py,sha256=cEg1oecBu4cKB69iJCqWEylC8b5XW47bl7rQiJsdTvM,1528
196
196
  flwr/server/app.py,sha256=QFnqrN1aeNTN-hmHt2YcqZNncAq89Qr2mlZtwITRIc4,24430
197
197
  flwr/server/client_manager.py,sha256=T8UDSRJBVD3fyIDI7NTAA-NA7GPrMNNgH2OAF54RRxE,6127
198
198
  flwr/server/client_proxy.py,sha256=4G-oTwhb45sfWLx2uZdcXD98IZwdTS6F88xe3akCdUg,2399
199
199
  flwr/server/compat/__init__.py,sha256=VxnJtJyOjNFQXMNi9hIuzNlZM5n0Hj1p3aq_Pm2udw4,892
200
- flwr/server/compat/app.py,sha256=u0elxfiLjGouCMQIy5KnCpeCHdc3s0qvojUm8unInIs,3421
200
+ flwr/server/compat/app.py,sha256=5vkHHm_h-4cMthvWD1GJo1ZW3eihytjGgvsgfXUK9gA,3298
201
201
  flwr/server/compat/app_utils.py,sha256=B9pec7LnYACzowXKZTZNu3SNS-fSaHfefwvRyAQa4Nc,3456
202
202
  flwr/server/compat/driver_client_proxy.py,sha256=BxTDo7i89VAG2tuF4x7zogSVn2bXPMr0H2H0lERzW9c,5444
203
203
  flwr/server/compat/legacy_context.py,sha256=wBzBcfV6YO6IQGriM_FdJ5XZfiBBEEJdS_OdAiF47dY,1804
204
204
  flwr/server/criterion.py,sha256=ypbAexbztzGUxNen9RCHF91QeqiEQix4t4Ih3E-42MM,1061
205
205
  flwr/server/driver/__init__.py,sha256=bikRv6CjTwSvYh7tf10gziU5o2YotOWhhftz2tr3KDc,886
206
206
  flwr/server/driver/driver.py,sha256=NT_yaeit7_kZEIsCEqOWPID1GrVD3ywH4xZ2wtIh5lM,5217
207
- flwr/server/driver/grpc_driver.py,sha256=EbnOtrTR4cmu2ZhmPqKEmOHAhNQD1fd0hAd_xN1MePQ,9749
207
+ flwr/server/driver/grpc_driver.py,sha256=qrCF0hi2AlKF_8R5N_W5aKoAxd80lN1xbdLTUVtpSO0,9648
208
208
  flwr/server/driver/inmemory_driver.py,sha256=RcK94_NtjGZ4aZDIscnU7A3Uv1u8jGx29-xcbjQvZTM,6444
209
209
  flwr/server/history.py,sha256=bBOHKyX1eQONIsUx4EUU-UnAk1i0EbEl8ioyMq_UWQ8,5063
210
210
  flwr/server/run_serverapp.py,sha256=Xw42zqNXmKGy3Fz5QjYOUS1MH7ULy2u9nad4QA8hh74,10479
@@ -254,7 +254,7 @@ flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py,sha256=h3EhqgelegVC4E
254
254
  flwr/server/superlink/fleet/grpc_bidi/grpc_server.py,sha256=DIXCYfPwm5paWq6hccReW0tB7-dTfq4vvMZ7CsiuYf4,12292
255
255
  flwr/server/superlink/fleet/grpc_rere/__init__.py,sha256=j2hyC342am-_Hgp1g80Y3fGDzfTI6n8QOOn2PyWf4eg,758
256
256
  flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py,sha256=bgoLQEhahVHjdlRDk_58zyKFeMOziiPUXSbYMhOxybY,4757
257
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py,sha256=paUSrLADNsMdN0anHdbEeGDvI_SMWjAkFSiP3SA-hx8,7933
257
+ flwr/server/superlink/fleet/grpc_rere/server_interceptor.py,sha256=zN8TIdg9ZxKtmrwtarWUPdm08jUBBGoz59ndO3C-7jg,7985
258
258
  flwr/server/superlink/fleet/message_handler/__init__.py,sha256=h8oLD7uo5lKICPy0rRdKRjTYe62u8PKkT_fA4xF5JPA,731
259
259
  flwr/server/superlink/fleet/message_handler/message_handler.py,sha256=9qDDPwj3txHPo2dNaWQiO3UpGno5Zm9IMhJXnAPZbqg,4439
260
260
  flwr/server/superlink/fleet/rest_rere/__init__.py,sha256=5jbYbAn75sGv-gBwOPDySE0kz96F6dTYLeMrGqNi4lM,735
@@ -262,7 +262,7 @@ flwr/server/superlink/fleet/rest_rere/rest_api.py,sha256=3wegOVJadA3pH_2CwZHQ3Uw
262
262
  flwr/server/superlink/fleet/vce/__init__.py,sha256=36MHKiefnJeyjwMQzVUK4m06Ojon3WDcwZGQsAcyVhQ,783
263
263
  flwr/server/superlink/fleet/vce/backend/__init__.py,sha256=psQjI1DQHOM5uWVXM27l_wPHjfEkMUTP-_8-lEFH1JA,1466
264
264
  flwr/server/superlink/fleet/vce/backend/backend.py,sha256=YugFH_XYclqmq07er5ne1DZc7PgR9lWGs5LY_YIHwc8,2207
265
- flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=GkeaSe7uXg2iIgKiZNTCwM3TAwYEzuJnr9mA8-vg4hM,6793
265
+ flwr/server/superlink/fleet/vce/backend/raybackend.py,sha256=VQ63rZytcavfDedfjArAejPi3VGYXrfcsb8yi57prRc,7168
266
266
  flwr/server/superlink/fleet/vce/vce_api.py,sha256=lTQ2WycKK4-w22EPoJJz1qn73j9ZTwBlNz2cla2Zv0Q,12653
267
267
  flwr/server/superlink/state/__init__.py,sha256=Gj2OTFLXvA-mAjBvwuKDM3rDrVaQPcIoybSa2uskMTE,1003
268
268
  flwr/server/superlink/state/in_memory_state.py,sha256=eNXOVt3HhJOYxkbYRNWEudCaNrYj2B7tOU9xgKbM2Ag,13166
@@ -283,19 +283,19 @@ flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=vdS_AeRYBv
283
283
  flwr/simulation/__init__.py,sha256=ybVBW3xT6cg_IasJV4_ymI3bVrCbFhw1mmcfrdfl3q0,1359
284
284
  flwr/simulation/app.py,sha256=te3dQB3eodPwzsv1y4daPyaskIAaOtgoHaQLobrqoqY,15163
285
285
  flwr/simulation/ray_transport/__init__.py,sha256=wzcEEwUUlulnXsg6raCA1nGpP3LlAQDtJ8zNkCXcVbA,734
286
- flwr/simulation/ray_transport/ray_actor.py,sha256=3j0HgzjrlYjnzdTRy8aA4Nf6VoUvxi1hGRQkGSU5z6c,19020
286
+ flwr/simulation/ray_transport/ray_actor.py,sha256=IbFwbRFxKHoLjgx_LTuehS0L3uHPw_SPaMfA4wHA0Xk,19026
287
287
  flwr/simulation/ray_transport/ray_client_proxy.py,sha256=0abIsU0VBk9rNJZOKHIyzYGy3ZnWBgqYocX_oct1EP0,7307
288
288
  flwr/simulation/ray_transport/utils.py,sha256=TYdtfg1P9VfTdLMOJlifInGpxWHYs9UfUqIv2wfkRLA,2392
289
- flwr/simulation/run_simulation.py,sha256=EeioiueoxuBompr04ETUSJp4nNuigamiUEObjVAZHV4,21722
290
- flwr/superexec/__init__.py,sha256=9h94ogLxi6eJ3bUuJYq3E3pApThSabTPiSmPAGlTkHE,800
291
- flwr/superexec/app.py,sha256=KQuAnyTs2RQMGeIOrJjR3fJ70HsIEF3LQRKazOy27OA,6489
289
+ flwr/simulation/run_simulation.py,sha256=hnH3qThfg_ii2tMu1y3X4ZWbw99CNk7vfduQ1fdZxbY,22750
290
+ flwr/superexec/__init__.py,sha256=fcj366jh4RFby_vDwLroU4kepzqbnJgseZD_jUr_Mko,715
291
+ flwr/superexec/app.py,sha256=gH25dr7FL9IWODSOtP0-r59iuOKDeSXLLPxpEAL50BM,6561
292
292
  flwr/superexec/deployment.py,sha256=1qhztkcZDjaSbicligbXGqn49gbpN271rTlEVAnNuWw,6283
293
293
  flwr/superexec/exec_grpc.py,sha256=PhqGoZEpTMxSQmUSV8Wgtzb1Za_pHJ-adZqo5RYnDyE,1942
294
294
  flwr/superexec/exec_servicer.py,sha256=jl0aKVjm0PLQABcTL5c3jdSIzb0Z6hpVOtrAn4Ob7ts,2323
295
295
  flwr/superexec/executor.py,sha256=k_adivto6R2U82DADOHNvdtobehBYreRek1gOEBIQnQ,2318
296
296
  flwr/superexec/simulation.py,sha256=J6pw-RqCSiUed8I_3MasZH4tl57ZmDebPAHNnbb0-vE,7420
297
- flwr_nightly-1.11.0.dev20240828.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
298
- flwr_nightly-1.11.0.dev20240828.dist-info/METADATA,sha256=DW4GNlzLFXJ5MQZC68Xdz7ie4OMwHW486Ci6x_G5--E,15703
299
- flwr_nightly-1.11.0.dev20240828.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
300
- flwr_nightly-1.11.0.dev20240828.dist-info/entry_points.txt,sha256=3cDQVJEBRCSLzJrVYAgjXpoCjuQ74I3A9NZ61DOHdVo,388
301
- flwr_nightly-1.11.0.dev20240828.dist-info/RECORD,,
297
+ flwr_nightly-1.11.1.dev20240911.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
298
+ flwr_nightly-1.11.1.dev20240911.dist-info/METADATA,sha256=96wpiwqS16Lsmrnng5_AdtynkNwGP4BsjBY0jRwKYYk,15703
299
+ flwr_nightly-1.11.1.dev20240911.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
300
+ flwr_nightly-1.11.1.dev20240911.dist-info/entry_points.txt,sha256=WUCbqhLEOzjx_lyATIM0-f0e8kOVaQjzwOvyOxHrMhs,434
301
+ flwr_nightly-1.11.1.dev20240911.dist-info/RECORD,,
@@ -0,0 +1,10 @@
1
+ [console_scripts]
2
+ flower-client-app=flwr.client.supernode:run_client_app
3
+ flower-server-app=flwr.server.run_serverapp:run_server_app
4
+ flower-simulation=flwr.simulation.run_simulation:run_simulation_from_cli
5
+ flower-superexec=flwr.superexec.app:run_superexec
6
+ flower-superlink=flwr.server.app:run_superlink
7
+ flower-supernode=flwr.client.supernode.app:run_supernode
8
+ flwr=flwr.cli.app:app
9
+ flwr-clientapp=flwr.client.clientapp:flwr_clientapp
10
+
@@ -1,10 +0,0 @@
1
- [console_scripts]
2
- flower-client-app=flwr.client:run_client_app
3
- flower-server-app=flwr.server:run_server_app
4
- flower-simulation=flwr.simulation.run_simulation:run_simulation_from_cli
5
- flower-superexec=flwr.superexec:run_superexec
6
- flower-superlink=flwr.server:run_superlink
7
- flower-supernode=flwr.client:run_supernode
8
- flwr=flwr.cli.app:app
9
- flwr-clientapp=flwr.client.clientapp:flwr_clientapp
10
-