flwr-nightly 1.20.0.dev20250725__py3-none-any.whl → 1.21.0.dev20250729__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.
Files changed (30) hide show
  1. flwr/cli/new/new.py +12 -4
  2. flwr/cli/new/templates/app/README.flowertune.md.tpl +2 -0
  3. flwr/cli/new/templates/app/README.md.tpl +5 -0
  4. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -3
  5. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +13 -1
  6. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +21 -2
  7. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +18 -1
  8. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +19 -2
  9. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +18 -1
  10. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +20 -3
  11. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +18 -1
  12. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +18 -1
  13. flwr/cli/utils.py +11 -3
  14. flwr/client/grpc_rere_client/connection.py +1 -2
  15. flwr/client/rest_client/connection.py +1 -2
  16. flwr/common/inflatable_utils.py +49 -6
  17. flwr/proto/appio_pb2.py +15 -19
  18. flwr/proto/appio_pb2.pyi +2 -17
  19. flwr/proto/fleet_pb2.py +8 -12
  20. flwr/proto/fleet_pb2.pyi +2 -17
  21. flwr/server/grid/grpc_grid.py +26 -24
  22. flwr/server/serverapp/app.py +6 -0
  23. flwr/simulation/app.py +8 -0
  24. flwr/simulation/run_simulation.py +17 -0
  25. flwr/supercore/object_store/utils.py +5 -10
  26. flwr/supernode/runtime/run_clientapp.py +1 -3
  27. {flwr_nightly-1.20.0.dev20250725.dist-info → flwr_nightly-1.21.0.dev20250729.dist-info}/METADATA +1 -1
  28. {flwr_nightly-1.20.0.dev20250725.dist-info → flwr_nightly-1.21.0.dev20250729.dist-info}/RECORD +30 -30
  29. {flwr_nightly-1.20.0.dev20250725.dist-info → flwr_nightly-1.21.0.dev20250729.dist-info}/WHEEL +0 -0
  30. {flwr_nightly-1.20.0.dev20250725.dist-info → flwr_nightly-1.21.0.dev20250729.dist-info}/entry_points.txt +0 -0
flwr/cli/new/new.py CHANGED
@@ -271,28 +271,36 @@ def new(
271
271
 
272
272
  prompt = typer.style(
273
273
  "🎊 Flower App creation successful.\n\n"
274
- "To run your Flower App, use the following command:\n\n",
274
+ "To run your Flower App, first install its dependencies:\n\n",
275
275
  fg=typer.colors.GREEN,
276
276
  bold=True,
277
277
  )
278
278
 
279
279
  _add = " huggingface-cli login\n" if llm_challenge_str else ""
280
+
280
281
  prompt += typer.style(
281
- _add + f" flwr run {package_name}\n\n",
282
+ f" cd {package_name} && pip install -e .\n" + _add + "\n",
282
283
  fg=typer.colors.BRIGHT_CYAN,
283
284
  bold=True,
284
285
  )
285
286
 
286
287
  prompt += typer.style(
287
- "If you haven't installed all dependencies yet, follow these steps:\n\n",
288
+ "then, run the app:\n\n ",
288
289
  fg=typer.colors.GREEN,
289
290
  bold=True,
290
291
  )
291
292
 
292
293
  prompt += typer.style(
293
- f" cd {package_name}\n" + " pip install -e .\n" + _add + " flwr run .\n",
294
+ "\tflwr run .\n\n",
294
295
  fg=typer.colors.BRIGHT_CYAN,
295
296
  bold=True,
296
297
  )
297
298
 
299
+ prompt += typer.style(
300
+ "💡 Check the README in your app directory to learn how to\n"
301
+ "customize it and how to run it using the Deployment Runtime.\n",
302
+ fg=typer.colors.GREEN,
303
+ bold=True,
304
+ )
305
+
298
306
  print(prompt)
@@ -21,6 +21,8 @@ Project dependencies are defined in `pyproject.toml`. Install them in an activat
21
21
  pip install -e .
22
22
  ```
23
23
 
24
+ > **Tip:** Learn how to configure your `pyproject.toml` file for Flower apps in [this guide](https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html).
25
+
24
26
  ## Experimental setup
25
27
 
26
28
  The dataset is divided into $num_clients partitions in an IID fashion, a partition is assigned to each ClientApp.
@@ -2,10 +2,15 @@
2
2
 
3
3
  ## Install dependencies and project
4
4
 
5
+ The dependencies are listed in the `pyproject.toml` and you can install them as follows:
6
+
5
7
  ```bash
6
8
  pip install -e .
7
9
  ```
8
10
 
11
+ > **Tip:** Your `pyproject.toml` file can define more than just the dependencies of your Flower app. You can also use it to specify hyperparameters for your runs and control which Flower Runtime is used. By default, it uses the Simulation Runtime, but you can switch to the Deployment Runtime when needed.
12
+ > Learn more in the [TOML configuration guide](https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html).
13
+
9
14
  ## Run with the Simulation Engine
10
15
 
11
16
  In the `$project_name` directory, use `flwr run` to run a local simulation:
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,11 +12,12 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "flwr-datasets[vision]>=0.5.0",
13
- "torch==2.6.0",
14
- "torchvision==0.21.0",
19
+ "torch==2.7.1",
20
+ "torchvision==0.22.1",
15
21
  ]
16
22
 
17
23
  [tool.hatch.metadata]
@@ -117,18 +123,23 @@ packages = ["."]
117
123
  [tool.flwr.app]
118
124
  publisher = "$username"
119
125
 
126
+ # Point to your ServerApp and ClientApp objects
127
+ # Format: "<module>:<object>"
120
128
  [tool.flwr.app.components]
121
129
  serverapp = "$import_name.server_app:app"
122
130
  clientapp = "$import_name.client_app:app"
123
131
 
132
+ # Custom config values accessible via `context.run_config`
124
133
  [tool.flwr.app.config]
125
134
  num-server-rounds = 3
126
135
  fraction-fit = 0.5
127
136
  local-epochs = 1
128
137
 
138
+ # Default federation to use when running the app
129
139
  [tool.flwr.federations]
130
140
  default = "local-simulation"
131
141
 
142
+ # Local simulation federation with 10 virtual SuperNodes
132
143
  [tool.flwr.federations.local-simulation]
133
144
  options.num-supernodes = 10
134
145
  options.backend.client-resources.num-cpus = 2
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,8 +12,9 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "flwr-datasets>=0.5.0",
13
19
  "torch==2.4.0",
14
20
  "trl==0.8.1",
@@ -27,10 +33,13 @@ packages = ["."]
27
33
  [tool.flwr.app]
28
34
  publisher = "$username"
29
35
 
36
+ # Point to your ServerApp and ClientApp objects
37
+ # Format: "<module>:<object>"
30
38
  [tool.flwr.app.components]
31
39
  serverapp = "$import_name.server_app:app"
32
40
  clientapp = "$import_name.client_app:app"
33
41
 
42
+ # Custom config values accessible via `context.run_config`
34
43
  [tool.flwr.app.config]
35
44
  model.name = "mistralai/Mistral-7B-v0.3"
36
45
  model.quantization = 4
@@ -56,12 +65,15 @@ strategy.fraction-fit = $fraction_fit
56
65
  strategy.fraction-evaluate = 0.0
57
66
  num-server-rounds = 200
58
67
 
68
+ # Dataset config (static for FlowerTune LLM Leaderboard)
59
69
  [tool.flwr.app.config.static]
60
70
  dataset.name = "$dataset_name"
61
71
 
72
+ # Default federation to use when running the app
62
73
  [tool.flwr.federations]
63
74
  default = "local-simulation"
64
75
 
76
+ # Local simulation federation with $num_clients virtual SuperNodes
65
77
  [tool.flwr.federations.local-simulation]
66
78
  options.num-supernodes = $num_clients
67
79
  options.backend.client-resources.num-cpus = 6
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,10 +12,11 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "flwr-datasets>=0.5.0",
13
- "torch==2.5.1",
19
+ "torch==2.7.1",
14
20
  "transformers>=4.30.0,<5.0",
15
21
  "evaluate>=0.4.0,<1.0",
16
22
  "datasets>=2.0.0, <3.0",
@@ -23,10 +29,13 @@ packages = ["."]
23
29
  [tool.flwr.app]
24
30
  publisher = "$username"
25
31
 
32
+ # Point to your ServerApp and ClientApp objects
33
+ # Format: "<module>:<object>"
26
34
  [tool.flwr.app.components]
27
35
  serverapp = "$import_name.server_app:app"
28
36
  clientapp = "$import_name.client_app:app"
29
37
 
38
+ # Custom config values accessible via `context.run_config`
30
39
  [tool.flwr.app.config]
31
40
  num-server-rounds = 3
32
41
  fraction-fit = 0.5
@@ -34,13 +43,23 @@ local-epochs = 1
34
43
  model-name = "prajjwal1/bert-tiny" # Set a larger model if you have access to more GPU resources
35
44
  num-labels = 2
36
45
 
46
+ # Default federation to use when running the app
37
47
  [tool.flwr.federations]
38
48
  default = "localhost"
39
49
 
50
+ # Local simulation federation with 10 virtual SuperNodes
40
51
  [tool.flwr.federations.localhost]
41
52
  options.num-supernodes = 10
42
53
 
54
+ # Local simulation federation with 10 virtual SuperNodes
55
+ # making use of GPUs
43
56
  [tool.flwr.federations.localhost-gpu]
44
57
  options.num-supernodes = 10
45
58
  options.backend.client-resources.num-cpus = 4 # each ClientApp assumes to use 4CPUs
46
59
  options.backend.client-resources.num-gpus = 0.25 # at most 4 ClientApps will run in a given GPU
60
+
61
+ # Remote federation example for use with SuperLink
62
+ [tool.flwr.federations.remote-federation]
63
+ address = "<SUPERLINK-ADDRESS>:<PORT>"
64
+ insecure = true # Remove this line to enable TLS
65
+ # root-certificates = "<PATH/TO/ca.crt>" # For TLS setup
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,8 +12,9 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "jax==0.4.30",
13
19
  "jaxlib==0.4.30",
14
20
  "scikit-learn==1.6.1",
@@ -20,16 +26,27 @@ packages = ["."]
20
26
  [tool.flwr.app]
21
27
  publisher = "$username"
22
28
 
29
+ # Point to your ServerApp and ClientApp objects
30
+ # Format: "<module>:<object>"
23
31
  [tool.flwr.app.components]
24
32
  serverapp = "$import_name.server_app:app"
25
33
  clientapp = "$import_name.client_app:app"
26
34
 
35
+ # Custom config values accessible via `context.run_config`
27
36
  [tool.flwr.app.config]
28
37
  num-server-rounds = 3
29
38
  input-dim = 3
30
39
 
40
+ # Default federation to use when running the app
31
41
  [tool.flwr.federations]
32
42
  default = "local-simulation"
33
43
 
44
+ # Local simulation federation with 10 virtual SuperNodes
34
45
  [tool.flwr.federations.local-simulation]
35
46
  options.num-supernodes = 10
47
+
48
+ # Remote federation example for use with SuperLink
49
+ [tool.flwr.federations.remote-federation]
50
+ address = "<SUPERLINK-ADDRESS>:<PORT>"
51
+ insecure = true # Remove this line to enable TLS
52
+ # root-certificates = "<PATH/TO/ca.crt>" # For TLS setup
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,10 +12,11 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "flwr-datasets[vision]>=0.5.0",
13
- "mlx==0.21.1",
19
+ "mlx==0.26.5",
14
20
  ]
15
21
 
16
22
  [tool.hatch.build.targets.wheel]
@@ -19,10 +25,13 @@ packages = ["."]
19
25
  [tool.flwr.app]
20
26
  publisher = "$username"
21
27
 
28
+ # Point to your ServerApp and ClientApp objects
29
+ # Format: "<module>:<object>"
22
30
  [tool.flwr.app.components]
23
31
  serverapp = "$import_name.server_app:app"
24
32
  clientapp = "$import_name.client_app:app"
25
33
 
34
+ # Custom config values accessible via `context.run_config`
26
35
  [tool.flwr.app.config]
27
36
  num-server-rounds = 3
28
37
  local-epochs = 1
@@ -32,8 +41,16 @@ hidden-dim = 32
32
41
  batch-size = 256
33
42
  lr = 0.1
34
43
 
44
+ # Default federation to use when running the app
35
45
  [tool.flwr.federations]
36
46
  default = "local-simulation"
37
47
 
48
+ # Local simulation federation with 10 virtual SuperNodes
38
49
  [tool.flwr.federations.local-simulation]
39
50
  options.num-supernodes = 10
51
+
52
+ # Remote federation example for use with SuperLink
53
+ [tool.flwr.federations.remote-federation]
54
+ address = "<SUPERLINK-ADDRESS>:<PORT>"
55
+ insecure = true # Remove this line to enable TLS
56
+ # root-certificates = "<PATH/TO/ca.crt>" # For TLS setup
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,8 +12,9 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "numpy>=2.0.2",
13
19
  ]
14
20
 
@@ -18,15 +24,26 @@ packages = ["."]
18
24
  [tool.flwr.app]
19
25
  publisher = "$username"
20
26
 
27
+ # Point to your ServerApp and ClientApp objects
28
+ # Format: "<module>:<object>"
21
29
  [tool.flwr.app.components]
22
30
  serverapp = "$import_name.server_app:app"
23
31
  clientapp = "$import_name.client_app:app"
24
32
 
33
+ # Custom config values accessible via `context.run_config`
25
34
  [tool.flwr.app.config]
26
35
  num-server-rounds = 3
27
36
 
37
+ # Default federation to use when running the app
28
38
  [tool.flwr.federations]
29
39
  default = "local-simulation"
30
40
 
41
+ # Local simulation federation with 10 virtual SuperNodes
31
42
  [tool.flwr.federations.local-simulation]
32
43
  options.num-supernodes = 10
44
+
45
+ # Remote federation example for use with SuperLink
46
+ [tool.flwr.federations.remote-federation]
47
+ address = "<SUPERLINK-ADDRESS>:<PORT>"
48
+ insecure = true # Remove this line to enable TLS
49
+ # root-certificates = "<PATH/TO/ca.crt>" # For TLS setup
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,11 +12,12 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "flwr-datasets[vision]>=0.5.0",
13
- "torch==2.5.1",
14
- "torchvision==0.20.1",
19
+ "torch==2.7.1",
20
+ "torchvision==0.22.1",
15
21
  ]
16
22
 
17
23
  [tool.hatch.build.targets.wheel]
@@ -20,17 +26,28 @@ packages = ["."]
20
26
  [tool.flwr.app]
21
27
  publisher = "$username"
22
28
 
29
+ # Point to your ServerApp and ClientApp objects
30
+ # Format: "<module>:<object>"
23
31
  [tool.flwr.app.components]
24
32
  serverapp = "$import_name.server_app:app"
25
33
  clientapp = "$import_name.client_app:app"
26
34
 
35
+ # Custom config values accessible via `context.run_config`
27
36
  [tool.flwr.app.config]
28
37
  num-server-rounds = 3
29
38
  fraction-fit = 0.5
30
39
  local-epochs = 1
31
40
 
41
+ # Default federation to use when running the app
32
42
  [tool.flwr.federations]
33
43
  default = "local-simulation"
34
44
 
45
+ # Local simulation federation with 10 virtual SuperNodes
35
46
  [tool.flwr.federations.local-simulation]
36
47
  options.num-supernodes = 10
48
+
49
+ # Remote federation example for use with SuperLink
50
+ [tool.flwr.federations.remote-federation]
51
+ address = "<SUPERLINK-ADDRESS>:<PORT>"
52
+ insecure = true # Remove this line to enable TLS
53
+ # root-certificates = "<PATH/TO/ca.crt>" # For TLS setup
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,8 +12,9 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "flwr-datasets[vision]>=0.5.0",
13
19
  "scikit-learn>=1.6.1",
14
20
  ]
@@ -19,17 +25,28 @@ packages = ["."]
19
25
  [tool.flwr.app]
20
26
  publisher = "$username"
21
27
 
28
+ # Point to your ServerApp and ClientApp objects
29
+ # Format: "<module>:<object>"
22
30
  [tool.flwr.app.components]
23
31
  serverapp = "$import_name.server_app:app"
24
32
  clientapp = "$import_name.client_app:app"
25
33
 
34
+ # Custom config values accessible via `context.run_config`
26
35
  [tool.flwr.app.config]
27
36
  num-server-rounds = 3
28
37
  penalty = "l2"
29
38
  local-epochs = 1
30
39
 
40
+ # Default federation to use when running the app
31
41
  [tool.flwr.federations]
32
42
  default = "local-simulation"
33
43
 
44
+ # Local simulation federation with 10 virtual SuperNodes
34
45
  [tool.flwr.federations.local-simulation]
35
46
  options.num-supernodes = 10
47
+
48
+ # Remote federation example for use with SuperLink
49
+ [tool.flwr.federations.remote-federation]
50
+ address = "<SUPERLINK-ADDRESS>:<PORT>"
51
+ insecure = true # Remove this line to enable TLS
52
+ # root-certificates = "<PATH/TO/ca.crt>" # For TLS setup
@@ -1,3 +1,8 @@
1
+ # =====================================================================
2
+ # For a full TOML configuration guide, check the Flower docs:
3
+ # https://flower.ai/docs/framework/how-to-configure-pyproject-toml.html
4
+ # =====================================================================
5
+
1
6
  [build-system]
2
7
  requires = ["hatchling"]
3
8
  build-backend = "hatchling.build"
@@ -7,8 +12,9 @@ name = "$package_name"
7
12
  version = "1.0.0"
8
13
  description = ""
9
14
  license = "Apache-2.0"
15
+ # Dependencies for your Flower App
10
16
  dependencies = [
11
- "flwr[simulation]>=1.20.0",
17
+ "flwr[simulation]>=1.21.0",
12
18
  "flwr-datasets[vision]>=0.5.0",
13
19
  "tensorflow>=2.11.1,<2.18.0",
14
20
  ]
@@ -19,18 +25,29 @@ packages = ["."]
19
25
  [tool.flwr.app]
20
26
  publisher = "$username"
21
27
 
28
+ # Point to your ServerApp and ClientApp objects
29
+ # Format: "<module>:<object>"
22
30
  [tool.flwr.app.components]
23
31
  serverapp = "$import_name.server_app:app"
24
32
  clientapp = "$import_name.client_app:app"
25
33
 
34
+ # Custom config values accessible via `context.run_config`
26
35
  [tool.flwr.app.config]
27
36
  num-server-rounds = 3
28
37
  local-epochs = 1
29
38
  batch-size = 32
30
39
  verbose = false
31
40
 
41
+ # Default federation to use when running the app
32
42
  [tool.flwr.federations]
33
43
  default = "local-simulation"
34
44
 
45
+ # Local simulation federation with 10 virtual SuperNodes
35
46
  [tool.flwr.federations.local-simulation]
36
47
  options.num-supernodes = 10
48
+
49
+ # Remote federation example for use with SuperLink
50
+ [tool.flwr.federations.remote-federation]
51
+ address = "<SUPERLINK-ADDRESS>:<PORT>"
52
+ insecure = true # Remove this line to enable TLS
53
+ # root-certificates = "<PATH/TO/ca.crt>" # For TLS setup
flwr/cli/utils.py CHANGED
@@ -296,9 +296,9 @@ def init_channel(
296
296
  def flwr_cli_grpc_exc_handler() -> Iterator[None]:
297
297
  """Context manager to handle specific gRPC errors.
298
298
 
299
- It catches grpc.RpcError exceptions with UNAUTHENTICATED, UNIMPLEMENTED, and
300
- PERMISSION_DENIED statuses, informs the user, and exits the application. All other
301
- exceptions will be allowed to escape.
299
+ It catches grpc.RpcError exceptions with UNAUTHENTICATED, UNIMPLEMENTED,
300
+ UNAVAILABLE, and PERMISSION_DENIED statuses, informs the user, and exits the
301
+ application. All other exceptions will be allowed to escape.
302
302
  """
303
303
  try:
304
304
  yield
@@ -327,6 +327,14 @@ def flwr_cli_grpc_exc_handler() -> Iterator[None]:
327
327
  # pylint: disable=E1101
328
328
  typer.secho(e.details(), fg=typer.colors.RED, bold=True)
329
329
  raise typer.Exit(code=1) from None
330
+ if e.code() == grpc.StatusCode.UNAVAILABLE:
331
+ typer.secho(
332
+ "Connection to the SuperLink is unavailable. Please check your network "
333
+ "connection and 'address' in the federation configuration.",
334
+ fg=typer.colors.RED,
335
+ bold=True,
336
+ )
337
+ raise typer.Exit(code=1) from None
330
338
  if (
331
339
  e.code() == grpc.StatusCode.NOT_FOUND
332
340
  and e.details() == RUN_ID_NOT_FOUND_MESSAGE
@@ -285,8 +285,7 @@ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
285
285
  response: PushMessagesResponse = stub.PushMessages(request=request)
286
286
 
287
287
  # Get and return the object IDs to push
288
- object_ids_to_push = response.objects_to_push[object_tree.object_id]
289
- return set(object_ids_to_push.object_ids)
288
+ return set(response.objects_to_push)
290
289
 
291
290
  def get_run(run_id: int) -> Run:
292
291
  # Call FleetAPI
@@ -369,8 +369,7 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
369
369
  raise ValueError("PushMessagesResponse is None.")
370
370
 
371
371
  # Get and return the object IDs to push
372
- object_ids_to_push = res.objects_to_push[object_tree.object_id]
373
- return set(object_ids_to_push.object_ids)
372
+ return set(res.objects_to_push)
374
373
 
375
374
  def get_run(run_id: int) -> Run:
376
375
  # Construct the request
@@ -34,6 +34,7 @@ from .constant import (
34
34
  PULL_MAX_TIME,
35
35
  PULL_MAX_TRIES_PER_OBJECT,
36
36
  )
37
+ from .exit_handlers import add_exit_handler
37
38
  from .inflatable import (
38
39
  InflatableObject,
39
40
  UnexpectedObjectContentError,
@@ -61,6 +62,34 @@ inflatable_class_registry: dict[str, type[InflatableObject]] = {
61
62
  T = TypeVar("T", bound=InflatableObject)
62
63
 
63
64
 
65
+ # Allow thread pool executors to be shut down gracefully
66
+ _thread_pool_executors: set[concurrent.futures.ThreadPoolExecutor] = set()
67
+ _lock = threading.Lock()
68
+
69
+
70
+ def _shutdown_thread_pool_executors() -> None:
71
+ """Shutdown all thread pool executors gracefully."""
72
+ with _lock:
73
+ for executor in _thread_pool_executors:
74
+ executor.shutdown(wait=False, cancel_futures=True)
75
+ _thread_pool_executors.clear()
76
+
77
+
78
+ def _track_executor(executor: concurrent.futures.ThreadPoolExecutor) -> None:
79
+ """Track a thread pool executor for graceful shutdown."""
80
+ with _lock:
81
+ _thread_pool_executors.add(executor)
82
+
83
+
84
+ def _untrack_executor(executor: concurrent.futures.ThreadPoolExecutor) -> None:
85
+ """Untrack a thread pool executor."""
86
+ with _lock:
87
+ _thread_pool_executors.discard(executor)
88
+
89
+
90
+ add_exit_handler(_shutdown_thread_pool_executors)
91
+
92
+
64
93
  class ObjectUnavailableError(Exception):
65
94
  """Exception raised when an object has been pre-registered but is not yet
66
95
  available."""
@@ -165,7 +194,16 @@ def push_object_contents_from_iterable(
165
194
  # Push all object contents concurrently
166
195
  num_workers = get_num_workers(max_concurrent_pushes)
167
196
  with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor:
168
- list(executor.map(push, object_contents))
197
+ # Ensure that the thread pool executors are tracked for graceful shutdown
198
+ _track_executor(executor)
199
+
200
+ # Submit push tasks for each object content
201
+ executor.map(push, object_contents) # Non-blocking map
202
+
203
+ # The context manager will block until all submitted tasks have completed
204
+
205
+ # Remove the executor from the list of tracked executors
206
+ _untrack_executor(executor)
169
207
 
170
208
 
171
209
  def pull_objects( # pylint: disable=too-many-arguments,too-many-locals
@@ -262,13 +300,18 @@ def pull_objects( # pylint: disable=too-many-arguments,too-many-locals
262
300
  # Submit all pull tasks concurrently
263
301
  num_workers = get_num_workers(max_concurrent_pulls)
264
302
  with concurrent.futures.ThreadPoolExecutor(max_workers=num_workers) as executor:
265
- futures = {
266
- executor.submit(pull_with_retries, obj_id): obj_id for obj_id in object_ids
267
- }
303
+ # Ensure that the thread pool executors are tracked for graceful shutdown
304
+ _track_executor(executor)
305
+
306
+ # Submit pull tasks for each object ID
307
+ executor.map(pull_with_retries, object_ids) # Non-blocking map
308
+
309
+ # The context manager will block until all submitted tasks have completed
268
310
 
269
- # Wait for completion
270
- concurrent.futures.wait(futures)
311
+ # Remove the executor from the list of tracked executors
312
+ _untrack_executor(executor)
271
313
 
314
+ # If an error occurred during pulling, raise it
272
315
  if err_to_raise is not None:
273
316
  raise err_to_raise
274
317
 
flwr/proto/appio_pb2.py CHANGED
@@ -17,31 +17,27 @@ from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
17
17
  from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
18
18
 
19
19
 
20
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/appio.proto\x12\nflwr.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x14\x66lwr/proto/run.proto\"\x99\x01\n\x16PushAppMessagesRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x0e\n\x06run_id\x18\x03 \x01(\x04\x12\x34\n\x14message_object_trees\x18\x04 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\xcc\x01\n\x17PushAppMessagesResponse\x12\x13\n\x0bmessage_ids\x18\x01 \x03(\t\x12O\n\x0fobjects_to_push\x18\x02 \x03(\x0b\x32\x36.flwr.proto.PushAppMessagesResponse.ObjectsToPushEntry\x1aK\n\x12ObjectsToPushEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12$\n\x05value\x18\x02 \x01(\x0b\x32\x15.flwr.proto.ObjectIDs:\x02\x38\x01\"L\n\x16PullAppMessagesRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x13\n\x0bmessage_ids\x18\x02 \x03(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\x04\"{\n\x17PullAppMessagesResponse\x12*\n\rmessages_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x02 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"%\n\x14PullAppInputsRequest\x12\r\n\x05token\x18\x01 \x01(\t\"y\n\x15PullAppInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"\\\n\x15PushAppOutputsRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\x04\x12$\n\x07\x63ontext\x18\x03 \x01(\x0b\x32\x13.flwr.proto.Context\"\x18\n\x16PushAppOutputsResponseb\x06proto3')
20
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/appio.proto\x12\nflwr.proto\x1a\x18\x66lwr/proto/message.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x14\x66lwr/proto/run.proto\"\x99\x01\n\x16PushAppMessagesRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x0e\n\x06run_id\x18\x03 \x01(\x04\x12\x34\n\x14message_object_trees\x18\x04 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"G\n\x17PushAppMessagesResponse\x12\x13\n\x0bmessage_ids\x18\x01 \x03(\t\x12\x17\n\x0fobjects_to_push\x18\x02 \x03(\t\"L\n\x16PullAppMessagesRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x13\n\x0bmessage_ids\x18\x02 \x03(\t\x12\x0e\n\x06run_id\x18\x03 \x01(\x04\"{\n\x17PullAppMessagesResponse\x12*\n\rmessages_list\x18\x01 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x02 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"%\n\x14PullAppInputsRequest\x12\r\n\x05token\x18\x01 \x01(\t\"y\n\x15PullAppInputsResponse\x12$\n\x07\x63ontext\x18\x01 \x01(\x0b\x32\x13.flwr.proto.Context\x12\x1c\n\x03run\x18\x02 \x01(\x0b\x32\x0f.flwr.proto.Run\x12\x1c\n\x03\x66\x61\x62\x18\x03 \x01(\x0b\x32\x0f.flwr.proto.Fab\"\\\n\x15PushAppOutputsRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x0e\n\x06run_id\x18\x02 \x01(\x04\x12$\n\x07\x63ontext\x18\x03 \x01(\x0b\x32\x13.flwr.proto.Context\"\x18\n\x16PushAppOutputsResponseb\x06proto3')
21
21
 
22
22
  _globals = globals()
23
23
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
24
24
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.appio_pb2', _globals)
25
25
  if _descriptor._USE_C_DESCRIPTORS == False:
26
26
  DESCRIPTOR._options = None
27
- _globals['_PUSHAPPMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._options = None
28
- _globals['_PUSHAPPMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._serialized_options = b'8\001'
29
27
  _globals['_PUSHAPPMESSAGESREQUEST']._serialized_start=109
30
28
  _globals['_PUSHAPPMESSAGESREQUEST']._serialized_end=262
31
- _globals['_PUSHAPPMESSAGESRESPONSE']._serialized_start=265
32
- _globals['_PUSHAPPMESSAGESRESPONSE']._serialized_end=469
33
- _globals['_PUSHAPPMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._serialized_start=394
34
- _globals['_PUSHAPPMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._serialized_end=469
35
- _globals['_PULLAPPMESSAGESREQUEST']._serialized_start=471
36
- _globals['_PULLAPPMESSAGESREQUEST']._serialized_end=547
37
- _globals['_PULLAPPMESSAGESRESPONSE']._serialized_start=549
38
- _globals['_PULLAPPMESSAGESRESPONSE']._serialized_end=672
39
- _globals['_PULLAPPINPUTSREQUEST']._serialized_start=674
40
- _globals['_PULLAPPINPUTSREQUEST']._serialized_end=711
41
- _globals['_PULLAPPINPUTSRESPONSE']._serialized_start=713
42
- _globals['_PULLAPPINPUTSRESPONSE']._serialized_end=834
43
- _globals['_PUSHAPPOUTPUTSREQUEST']._serialized_start=836
44
- _globals['_PUSHAPPOUTPUTSREQUEST']._serialized_end=928
45
- _globals['_PUSHAPPOUTPUTSRESPONSE']._serialized_start=930
46
- _globals['_PUSHAPPOUTPUTSRESPONSE']._serialized_end=954
29
+ _globals['_PUSHAPPMESSAGESRESPONSE']._serialized_start=264
30
+ _globals['_PUSHAPPMESSAGESRESPONSE']._serialized_end=335
31
+ _globals['_PULLAPPMESSAGESREQUEST']._serialized_start=337
32
+ _globals['_PULLAPPMESSAGESREQUEST']._serialized_end=413
33
+ _globals['_PULLAPPMESSAGESRESPONSE']._serialized_start=415
34
+ _globals['_PULLAPPMESSAGESRESPONSE']._serialized_end=538
35
+ _globals['_PULLAPPINPUTSREQUEST']._serialized_start=540
36
+ _globals['_PULLAPPINPUTSREQUEST']._serialized_end=577
37
+ _globals['_PULLAPPINPUTSRESPONSE']._serialized_start=579
38
+ _globals['_PULLAPPINPUTSRESPONSE']._serialized_end=700
39
+ _globals['_PUSHAPPOUTPUTSREQUEST']._serialized_start=702
40
+ _globals['_PUSHAPPOUTPUTSREQUEST']._serialized_end=794
41
+ _globals['_PUSHAPPOUTPUTSRESPONSE']._serialized_start=796
42
+ _globals['_PUSHAPPOUTPUTSRESPONSE']._serialized_end=820
47
43
  # @@protoc_insertion_point(module_scope)
flwr/proto/appio_pb2.pyi CHANGED
@@ -42,31 +42,16 @@ global___PushAppMessagesRequest = PushAppMessagesRequest
42
42
 
43
43
  class PushAppMessagesResponse(google.protobuf.message.Message):
44
44
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
45
- class ObjectsToPushEntry(google.protobuf.message.Message):
46
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
47
- KEY_FIELD_NUMBER: builtins.int
48
- VALUE_FIELD_NUMBER: builtins.int
49
- key: typing.Text
50
- @property
51
- def value(self) -> flwr.proto.message_pb2.ObjectIDs: ...
52
- def __init__(self,
53
- *,
54
- key: typing.Text = ...,
55
- value: typing.Optional[flwr.proto.message_pb2.ObjectIDs] = ...,
56
- ) -> None: ...
57
- def HasField(self, field_name: typing_extensions.Literal["value",b"value"]) -> builtins.bool: ...
58
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
59
-
60
45
  MESSAGE_IDS_FIELD_NUMBER: builtins.int
61
46
  OBJECTS_TO_PUSH_FIELD_NUMBER: builtins.int
62
47
  @property
63
48
  def message_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ...
64
49
  @property
65
- def objects_to_push(self) -> google.protobuf.internal.containers.MessageMap[typing.Text, flwr.proto.message_pb2.ObjectIDs]: ...
50
+ def objects_to_push(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ...
66
51
  def __init__(self,
67
52
  *,
68
53
  message_ids: typing.Optional[typing.Iterable[typing.Text]] = ...,
69
- objects_to_push: typing.Optional[typing.Mapping[typing.Text, flwr.proto.message_pb2.ObjectIDs]] = ...,
54
+ objects_to_push: typing.Optional[typing.Iterable[typing.Text]] = ...,
70
55
  ) -> None: ...
71
56
  def ClearField(self, field_name: typing_extensions.Literal["message_ids",b"message_ids","objects_to_push",b"objects_to_push"]) -> None: ...
72
57
  global___PushAppMessagesResponse = PushAppMessagesResponse
flwr/proto/fleet_pb2.py CHANGED
@@ -19,7 +19,7 @@ from flwr.proto import fab_pb2 as flwr_dot_proto_dot_fab__pb2
19
19
  from flwr.proto import message_pb2 as flwr_dot_proto_dot_message__pb2
20
20
 
21
21
 
22
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x18\x66lwr/proto/message.proto\"/\n\x11\x43reateNodeRequest\x12\x1a\n\x12heartbeat_interval\x18\x01 \x01(\x01\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\"J\n\x13PullMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x13\n\x0bmessage_ids\x18\x02 \x03(\t\"\xa2\x01\n\x14PullMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\x97\x01\n\x13PushMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\xcb\x02\n\x14PushMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12>\n\x07results\x18\x02 \x03(\x0b\x32-.flwr.proto.PushMessagesResponse.ResultsEntry\x12L\n\x0fobjects_to_push\x18\x03 \x03(\x0b\x32\x33.flwr.proto.PushMessagesResponse.ObjectsToPushEntry\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\x1aK\n\x12ObjectsToPushEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12$\n\x05value\x18\x02 \x01(\x0b\x32\x15.flwr.proto.ObjectIDs:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\xca\x06\n\x05\x46leet\x12M\n\nCreateNode\x12\x1d.flwr.proto.CreateNodeRequest\x1a\x1e.flwr.proto.CreateNodeResponse\"\x00\x12M\n\nDeleteNode\x12\x1d.flwr.proto.DeleteNodeRequest\x1a\x1e.flwr.proto.DeleteNodeResponse\"\x00\x12\x62\n\x11SendNodeHeartbeat\x12$.flwr.proto.SendNodeHeartbeatRequest\x1a%.flwr.proto.SendNodeHeartbeatResponse\"\x00\x12S\n\x0cPullMessages\x12\x1f.flwr.proto.PullMessagesRequest\x1a .flwr.proto.PullMessagesResponse\"\x00\x12S\n\x0cPushMessages\x12\x1f.flwr.proto.PushMessagesRequest\x1a .flwr.proto.PushMessagesResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x12M\n\nPushObject\x12\x1d.flwr.proto.PushObjectRequest\x1a\x1e.flwr.proto.PushObjectResponse\"\x00\x12M\n\nPullObject\x12\x1d.flwr.proto.PullObjectRequest\x1a\x1e.flwr.proto.PullObjectResponse\"\x00\x12q\n\x16\x43onfirmMessageReceived\x12).flwr.proto.ConfirmMessageReceivedRequest\x1a*.flwr.proto.ConfirmMessageReceivedResponse\"\x00\x62\x06proto3')
22
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x66lwr/proto/fleet.proto\x12\nflwr.proto\x1a\x1a\x66lwr/proto/heartbeat.proto\x1a\x15\x66lwr/proto/node.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x14\x66lwr/proto/fab.proto\x1a\x18\x66lwr/proto/message.proto\"/\n\x11\x43reateNodeRequest\x12\x1a\n\x12heartbeat_interval\x18\x01 \x01(\x01\"4\n\x12\x43reateNodeResponse\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"3\n\x11\x44\x65leteNodeRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\"\x14\n\x12\x44\x65leteNodeResponse\"J\n\x13PullMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12\x13\n\x0bmessage_ids\x18\x02 \x03(\t\"\xa2\x01\n\x14PullMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\x97\x01\n\x13PushMessagesRequest\x12\x1e\n\x04node\x18\x01 \x01(\x0b\x32\x10.flwr.proto.Node\x12*\n\rmessages_list\x18\x02 \x03(\x0b\x32\x13.flwr.proto.Message\x12\x34\n\x14message_object_trees\x18\x03 \x03(\x0b\x32\x16.flwr.proto.ObjectTree\"\xc9\x01\n\x14PushMessagesResponse\x12(\n\treconnect\x18\x01 \x01(\x0b\x32\x15.flwr.proto.Reconnect\x12>\n\x07results\x18\x02 \x03(\x0b\x32-.flwr.proto.PushMessagesResponse.ResultsEntry\x12\x17\n\x0fobjects_to_push\x18\x03 \x03(\t\x1a.\n\x0cResultsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\x1e\n\tReconnect\x12\x11\n\treconnect\x18\x01 \x01(\x04\x32\xca\x06\n\x05\x46leet\x12M\n\nCreateNode\x12\x1d.flwr.proto.CreateNodeRequest\x1a\x1e.flwr.proto.CreateNodeResponse\"\x00\x12M\n\nDeleteNode\x12\x1d.flwr.proto.DeleteNodeRequest\x1a\x1e.flwr.proto.DeleteNodeResponse\"\x00\x12\x62\n\x11SendNodeHeartbeat\x12$.flwr.proto.SendNodeHeartbeatRequest\x1a%.flwr.proto.SendNodeHeartbeatResponse\"\x00\x12S\n\x0cPullMessages\x12\x1f.flwr.proto.PullMessagesRequest\x1a .flwr.proto.PullMessagesResponse\"\x00\x12S\n\x0cPushMessages\x12\x1f.flwr.proto.PushMessagesRequest\x1a .flwr.proto.PushMessagesResponse\"\x00\x12\x41\n\x06GetRun\x12\x19.flwr.proto.GetRunRequest\x1a\x1a.flwr.proto.GetRunResponse\"\x00\x12\x41\n\x06GetFab\x12\x19.flwr.proto.GetFabRequest\x1a\x1a.flwr.proto.GetFabResponse\"\x00\x12M\n\nPushObject\x12\x1d.flwr.proto.PushObjectRequest\x1a\x1e.flwr.proto.PushObjectResponse\"\x00\x12M\n\nPullObject\x12\x1d.flwr.proto.PullObjectRequest\x1a\x1e.flwr.proto.PullObjectResponse\"\x00\x12q\n\x16\x43onfirmMessageReceived\x12).flwr.proto.ConfirmMessageReceivedRequest\x1a*.flwr.proto.ConfirmMessageReceivedResponse\"\x00\x62\x06proto3')
23
23
 
24
24
  _globals = globals()
25
25
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -28,8 +28,6 @@ if _descriptor._USE_C_DESCRIPTORS == False:
28
28
  DESCRIPTOR._options = None
29
29
  _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._options = None
30
30
  _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_options = b'8\001'
31
- _globals['_PUSHMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._options = None
32
- _globals['_PUSHMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._serialized_options = b'8\001'
33
31
  _globals['_CREATENODEREQUEST']._serialized_start=159
34
32
  _globals['_CREATENODEREQUEST']._serialized_end=206
35
33
  _globals['_CREATENODERESPONSE']._serialized_start=208
@@ -45,13 +43,11 @@ if _descriptor._USE_C_DESCRIPTORS == False:
45
43
  _globals['_PUSHMESSAGESREQUEST']._serialized_start=579
46
44
  _globals['_PUSHMESSAGESREQUEST']._serialized_end=730
47
45
  _globals['_PUSHMESSAGESRESPONSE']._serialized_start=733
48
- _globals['_PUSHMESSAGESRESPONSE']._serialized_end=1064
49
- _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_start=941
50
- _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_end=987
51
- _globals['_PUSHMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._serialized_start=989
52
- _globals['_PUSHMESSAGESRESPONSE_OBJECTSTOPUSHENTRY']._serialized_end=1064
53
- _globals['_RECONNECT']._serialized_start=1066
54
- _globals['_RECONNECT']._serialized_end=1096
55
- _globals['_FLEET']._serialized_start=1099
56
- _globals['_FLEET']._serialized_end=1941
46
+ _globals['_PUSHMESSAGESRESPONSE']._serialized_end=934
47
+ _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_start=888
48
+ _globals['_PUSHMESSAGESRESPONSE_RESULTSENTRY']._serialized_end=934
49
+ _globals['_RECONNECT']._serialized_start=936
50
+ _globals['_RECONNECT']._serialized_end=966
51
+ _globals['_FLEET']._serialized_start=969
52
+ _globals['_FLEET']._serialized_end=1811
57
53
  # @@protoc_insertion_point(module_scope)
flwr/proto/fleet_pb2.pyi CHANGED
@@ -134,21 +134,6 @@ class PushMessagesResponse(google.protobuf.message.Message):
134
134
  ) -> None: ...
135
135
  def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
136
136
 
137
- class ObjectsToPushEntry(google.protobuf.message.Message):
138
- DESCRIPTOR: google.protobuf.descriptor.Descriptor
139
- KEY_FIELD_NUMBER: builtins.int
140
- VALUE_FIELD_NUMBER: builtins.int
141
- key: typing.Text
142
- @property
143
- def value(self) -> flwr.proto.message_pb2.ObjectIDs: ...
144
- def __init__(self,
145
- *,
146
- key: typing.Text = ...,
147
- value: typing.Optional[flwr.proto.message_pb2.ObjectIDs] = ...,
148
- ) -> None: ...
149
- def HasField(self, field_name: typing_extensions.Literal["value",b"value"]) -> builtins.bool: ...
150
- def ClearField(self, field_name: typing_extensions.Literal["key",b"key","value",b"value"]) -> None: ...
151
-
152
137
  RECONNECT_FIELD_NUMBER: builtins.int
153
138
  RESULTS_FIELD_NUMBER: builtins.int
154
139
  OBJECTS_TO_PUSH_FIELD_NUMBER: builtins.int
@@ -157,12 +142,12 @@ class PushMessagesResponse(google.protobuf.message.Message):
157
142
  @property
158
143
  def results(self) -> google.protobuf.internal.containers.ScalarMap[typing.Text, builtins.int]: ...
159
144
  @property
160
- def objects_to_push(self) -> google.protobuf.internal.containers.MessageMap[typing.Text, flwr.proto.message_pb2.ObjectIDs]: ...
145
+ def objects_to_push(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[typing.Text]: ...
161
146
  def __init__(self,
162
147
  *,
163
148
  reconnect: typing.Optional[global___Reconnect] = ...,
164
149
  results: typing.Optional[typing.Mapping[typing.Text, builtins.int]] = ...,
165
- objects_to_push: typing.Optional[typing.Mapping[typing.Text, flwr.proto.message_pb2.ObjectIDs]] = ...,
150
+ objects_to_push: typing.Optional[typing.Iterable[typing.Text]] = ...,
166
151
  ) -> None: ...
167
152
  def HasField(self, field_name: typing_extensions.Literal["reconnect",b"reconnect"]) -> builtins.bool: ...
168
153
  def ClearField(self, field_name: typing_extensions.Literal["objects_to_push",b"objects_to_push","reconnect",b"reconnect","results",b"results"]) -> None: ...
@@ -32,6 +32,7 @@ from flwr.common.constant import (
32
32
  )
33
33
  from flwr.common.grpc import create_channel, on_channel_state_change
34
34
  from flwr.common.inflatable import (
35
+ InflatableObject,
35
36
  get_all_nested_objects,
36
37
  get_object_tree,
37
38
  iterate_object_tree,
@@ -222,37 +223,38 @@ class GrpcGrid(Grid):
222
223
  )
223
224
  return [node.node_id for node in res.nodes]
224
225
 
225
- def _try_push_message(self, run_id: int, message: Message) -> str:
226
- """Push one message and its associated objects."""
227
- # Compute mapping of message descendants
228
- all_objects = get_all_nested_objects(message)
229
- msg_id = message.object_id
230
- object_tree = get_object_tree(message)
226
+ def _try_push_messages(self, run_id: int, messages: Iterable[Message]) -> list[str]:
227
+ """Push all messages and its associated objects."""
228
+ # Prepare all Messages to be sent in a single request
229
+ proto_messages = []
230
+ object_trees = []
231
+ all_objects: dict[str, InflatableObject] = {}
232
+ for msg in messages:
233
+ proto_messages.append(message_to_proto(remove_content_from_message(msg)))
234
+ all_objects.update(get_all_nested_objects(msg))
235
+ object_trees.append(get_object_tree(msg))
236
+ del msg
231
237
 
232
238
  # Call GrpcServerAppIoStub method
233
239
  res: PushAppMessagesResponse = self._stub.PushMessages(
234
240
  PushAppMessagesRequest(
235
- messages_list=[message_to_proto(remove_content_from_message(message))],
241
+ messages_list=proto_messages,
236
242
  run_id=run_id,
237
- message_object_trees=[object_tree],
243
+ message_object_trees=object_trees,
238
244
  )
239
245
  )
240
246
 
241
247
  # Push objects
242
- # If Message was added to the LinkState correctly
243
- if msg_id is not None:
244
- obj_ids_to_push = set(res.objects_to_push[msg_id].object_ids)
245
- # Push only object that are not in the store
246
- push_objects(
247
- all_objects,
248
- push_object_fn=make_push_object_fn_protobuf(
249
- push_object_protobuf=self._stub.PushObject,
250
- node=self.node,
251
- run_id=run_id,
252
- ),
253
- object_ids_to_push=obj_ids_to_push,
254
- )
255
- return msg_id
248
+ push_objects(
249
+ all_objects,
250
+ push_object_fn=make_push_object_fn_protobuf(
251
+ push_object_protobuf=self._stub.PushObject,
252
+ node=self.node,
253
+ run_id=run_id,
254
+ ),
255
+ object_ids_to_push=set(res.objects_to_push),
256
+ )
257
+ return cast(list[str], res.message_ids)
256
258
 
257
259
  def push_messages(self, messages: Iterable[Message]) -> Iterable[str]:
258
260
  """Push messages to specified node IDs.
@@ -272,8 +274,8 @@ class GrpcGrid(Grid):
272
274
  msg.metadata.__dict__["_message_id"] = msg.object_id
273
275
  # Check message
274
276
  self._check_message(msg)
275
- # Try pushing message and its objects
276
- message_ids.append(self._try_push_message(run_id, msg))
277
+ # Try pushing messages and their objects
278
+ message_ids = self._try_push_messages(run_id, messages)
277
279
 
278
280
  except grpc.RpcError as e:
279
281
  if e.code() == grpc.StatusCode.RESOURCE_EXHAUSTED: # pylint: disable=E1101
@@ -16,6 +16,7 @@
16
16
 
17
17
 
18
18
  import argparse
19
+ import gc
19
20
  from logging import DEBUG, ERROR, INFO
20
21
  from pathlib import Path
21
22
  from queue import Queue
@@ -115,6 +116,7 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
115
116
  run_status = None
116
117
  heartbeat_sender = None
117
118
  grid = None
119
+ context = None
118
120
  while True:
119
121
 
120
122
  try:
@@ -248,6 +250,10 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
248
250
  if grid:
249
251
  grid.close()
250
252
 
253
+ # Clean up the Context
254
+ context = None
255
+ gc.collect()
256
+
251
257
  event(
252
258
  EventType.FLWR_SERVERAPP_RUN_LEAVE,
253
259
  event_details={"run-id-hash": hash_run_id, "success": success},
flwr/simulation/app.py CHANGED
@@ -16,6 +16,7 @@
16
16
 
17
17
 
18
18
  import argparse
19
+ import gc
19
20
  from logging import DEBUG, ERROR, INFO
20
21
  from queue import Queue
21
22
  from time import sleep
@@ -271,6 +272,13 @@ def run_simulation_process( # pylint: disable=R0914, disable=W0212, disable=R09
271
272
  )
272
273
  )
273
274
 
275
+ # Clean up the Context if it exists
276
+ try:
277
+ del updated_context
278
+ except NameError:
279
+ pass
280
+ gc.collect()
281
+
274
282
  # Stop the loop if `flwr-simulation` is expected to process a single run
275
283
  if run_once:
276
284
  break
@@ -19,6 +19,7 @@ import argparse
19
19
  import asyncio
20
20
  import json
21
21
  import logging
22
+ import platform
22
23
  import sys
23
24
  import threading
24
25
  import traceback
@@ -63,6 +64,18 @@ def _replace_keys(d: Any, match: str, target: str) -> Any:
63
64
  return d
64
65
 
65
66
 
67
+ def _check_ray_support(backend_name: str) -> None:
68
+ if backend_name.lower() == "ray":
69
+ if platform.system() == "Windows":
70
+ log(
71
+ WARNING,
72
+ "Ray support on Windows is experimental "
73
+ "and may not work as expected. "
74
+ "On Windows, Flower Simulations run best in WSL2: "
75
+ "https://learn.microsoft.com/en-us/windows/wsl/about",
76
+ )
77
+
78
+
66
79
  # Entry point from CLI
67
80
  # pylint: disable=too-many-locals
68
81
  def run_simulation_from_cli() -> None:
@@ -82,6 +95,8 @@ def run_simulation_from_cli() -> None:
82
95
  code_example='TF_FORCE_GPU_ALLOW_GROWTH="true" flower-simulation <...>',
83
96
  )
84
97
 
98
+ _check_ray_support(args.backend)
99
+
85
100
  # Load JSON config
86
101
  backend_config_dict = json.loads(args.backend_config)
87
102
 
@@ -208,6 +223,8 @@ def run_simulation(
208
223
  "\n\tflwr.simulation.run_simulationt(...)",
209
224
  )
210
225
 
226
+ _check_ray_support(backend_name)
227
+
211
228
  _ = _run_simulation(
212
229
  num_supernodes=num_supernodes,
213
230
  client_app=client_app,
@@ -19,30 +19,25 @@ from typing import Union
19
19
 
20
20
  from flwr.proto.appio_pb2 import PushAppMessagesRequest # pylint: disable=E0611
21
21
  from flwr.proto.fleet_pb2 import PushMessagesRequest # pylint: disable=E0611
22
- from flwr.proto.message_pb2 import ObjectIDs # pylint: disable=E0611
23
22
 
24
23
  from . import ObjectStore
25
24
 
26
25
 
27
26
  def store_mapping_and_register_objects(
28
27
  store: ObjectStore, request: Union[PushAppMessagesRequest, PushMessagesRequest]
29
- ) -> dict[str, ObjectIDs]:
28
+ ) -> set[str]:
30
29
  """Store Message object to descendants mapping and preregister objects."""
31
30
  if not request.messages_list:
32
- return {}
33
-
34
- objects_to_push: dict[str, ObjectIDs] = {}
35
-
31
+ return set()
32
+ objects_to_push: set[str] = set()
36
33
  # Get run_id from the first message in the list
37
34
  # All messages of a request should in the same run
38
35
  run_id = request.messages_list[0].metadata.run_id
39
36
 
40
37
  for object_tree in request.message_object_trees:
41
38
  # Preregister
42
- object_ids_just_registered = store.preregister(run_id, object_tree)
39
+ unavailable_obj_ids = store.preregister(run_id, object_tree)
43
40
  # Keep track of objects that need to be pushed
44
- objects_to_push[object_tree.object_id] = ObjectIDs(
45
- object_ids=object_ids_just_registered
46
- )
41
+ objects_to_push |= set(unavailable_obj_ids)
47
42
 
48
43
  return objects_to_push
@@ -278,9 +278,7 @@ def push_clientappoutputs(
278
278
  del proto_message
279
279
 
280
280
  # Retrieve the object IDs to push
281
- object_ids_to_push = set(
282
- push_msg_res.objects_to_push[object_tree.object_id].object_ids
283
- )
281
+ object_ids_to_push = set(push_msg_res.objects_to_push)
284
282
 
285
283
  # Push all objects
286
284
  all_objects = get_all_nested_objects(message)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.20.0.dev20250725
3
+ Version: 1.21.0.dev20250729
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  License: Apache-2.0
6
6
  Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
@@ -17,13 +17,13 @@ flwr/cli/login/__init__.py,sha256=B1SXKU3HCQhWfFDMJhlC7FOl8UsvH4mxysxeBnrfyUE,80
17
17
  flwr/cli/login/login.py,sha256=pDkx5o4XBFBi7aGp_0Ys2FUMKmcWjzwWdoQRumXUsd8,4297
18
18
  flwr/cli/ls.py,sha256=zBBO3Yom3DIYwIJg09OSxy347wiYffOviqh-c2U4A04,10939
19
19
  flwr/cli/new/__init__.py,sha256=QA1E2QtzPvFCjLTUHnFnJbufuFiGyT_0Y53Wpbvg1F0,790
20
- flwr/cli/new/new.py,sha256=2e4ACJqeZ0W0_FyksQTi7PEzQpXT8KRpBPthFoac6zQ,9917
20
+ flwr/cli/new/new.py,sha256=Y8m4t8SmI_myr1tAcncHPE1_-y-dpn6BwKIVW1wj8xk,10071
21
21
  flwr/cli/new/templates/__init__.py,sha256=FpjWCfIySU2DB4kh0HOXLAjlZNNFDTVU4w3HoE2TzcI,725
22
22
  flwr/cli/new/templates/app/.gitignore.tpl,sha256=HZJcGQoxp7aUzaPg8Uqch3kNrIESwr9yjimDxJYgXVY,3104
23
23
  flwr/cli/new/templates/app/LICENSE.tpl,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
24
24
  flwr/cli/new/templates/app/README.baseline.md.tpl,sha256=oClo5eR0iLuPzBT7uS3ikhNRAnySz_lhkHFElixKyJM,9640
25
- flwr/cli/new/templates/app/README.flowertune.md.tpl,sha256=QSG51uifue2KVZz2ZNw8kmOStS7svC2AQ2gTa5E7Bhs,3326
26
- flwr/cli/new/templates/app/README.md.tpl,sha256=qZ6XHeCdSoDwtJqgcyi-ChgOCLMwQ3E42rcY-9qhlUY,1392
25
+ flwr/cli/new/templates/app/README.flowertune.md.tpl,sha256=sZFKxfvWvCopsTA_z0w5mvwepP0ksVslr5R-43SRBAI,3492
26
+ flwr/cli/new/templates/app/README.md.tpl,sha256=uI8vUMCEGA1-WJ8-9EUFCnNtofWFkJNOxaR1V6r_Su4,1908
27
27
  flwr/cli/new/templates/app/__init__.py,sha256=LbR0ksGiF566JcHM_H5m1Tc4-oYUEilWFlcXR-Oe6bI,729
28
28
  flwr/cli/new/templates/app/code/__init__.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
29
29
  flwr/cli/new/templates/app/code/__init__.py,sha256=zXa2YU1swzHxOKDQbwlDMEwVPOUswVeosjkiXNMTgFo,736
@@ -61,19 +61,19 @@ flwr/cli/new/templates/app/code/task.pytorch.py.tpl,sha256=XlJqA4Ix_PloO_zJLhjiN
61
61
  flwr/cli/new/templates/app/code/task.sklearn.py.tpl,sha256=vHdhtMp0FHxbYafXyhDT9aKmmmA0Jvpx5Oum1Yu9lWY,1850
62
62
  flwr/cli/new/templates/app/code/task.tensorflow.py.tpl,sha256=SKXAZdgBnPpbAbJ90Rb7oQ5ilnopBx_j_JNFoUDeEAI,1732
63
63
  flwr/cli/new/templates/app/code/utils.baseline.py.tpl,sha256=YkHAgppUeD2BnBoGfVB6dEvBfjuIPGsU1gw4CiUi3qA,40
64
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl,sha256=NVZBJZwn2NpHe6_4Cm32IRy7135ZI1zJQxCdW4_MPeg,2623
65
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=nth0gqYOteImRFKktdC_UQ-EGIl1qHJyiBsMH9orV-I,1873
66
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl,sha256=gtCxa-eoE3AAGBKFXWXcD1-5Ef4I0J8-DGafDW8BqXE,1143
67
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=oMuJKx1gH-Vn1PhkbpIfz8W18ktycaDGzdiOuUEmW6U,673
68
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=PlYcCItFBVRElU5KQgzO6xjmC4FDcN7tUR8BK3YBFbA,744
69
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=PL32GQmsxKOM7p4uEzM4jLeKqJs73YPR4P27a999y1Q,611
70
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=7zEuhCEz7MqfwS40udx-DP3yBuT8r927GiT6vb0gsbg,710
71
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=xXXS2D6avnDNxF906rKgdi5UPNPVDI-_p_3zz5MqcDI,686
72
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=3-gDei-K7zskh5dDuhPERkkMekbxGffRN_qZyNHiQH4,710
64
+ flwr/cli/new/templates/app/pyproject.baseline.toml.tpl,sha256=o7WJPF7lH_azNTiUrvTtLfXKyAY3_xTzwvUpkp3suUI,3180
65
+ flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl,sha256=zDNuDkvgVI7SNK7RLSlaFs2LhSW1-THDKNBJVw1_y78,2497
66
+ flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl,sha256=dsjlA0aS-U7xzB7YCYMOGVb7a0XJTB5bsemFzwUPkPg,2019
67
+ flwr/cli/new/templates/app/pyproject.jax.toml.tpl,sha256=BHGb2N57Xm2scO1mqDSNgK_aiI68mRh6u4Y9TLL5bZE,1471
68
+ flwr/cli/new/templates/app/pyproject.mlx.toml.tpl,sha256=O6eN6Zqx2ieh-WbaiMYmxfhCrLKJjwymn_nhUDEOldM,1542
69
+ flwr/cli/new/templates/app/pyproject.numpy.toml.tpl,sha256=oMTI8qXSgQsGlZextUpkWFvNOMlnWbzn2EocPSwDrtw,1409
70
+ flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl,sha256=bu65oSrM85fP_H0-RlMS2i8XgL_8O5TfSHLW87lb30s,1508
71
+ flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl,sha256=mAEPeBfGyrINgRuP6-nX_KJNTQjC4E5N1Nrcddxiffs,1484
72
+ flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl,sha256=mK8wOWqoQOVxZG6-OVwA2ChmKxexC7TfQV0ztPE4BWY,1508
73
73
  flwr/cli/run/__init__.py,sha256=RPyB7KbYTFl6YRiilCch6oezxrLQrl1kijV7BMGkLbA,790
74
74
  flwr/cli/run/run.py,sha256=psmr215gkV0e0QtX9NFp7KUwKSA_ZwekdJmoL1zFyfw,8478
75
75
  flwr/cli/stop.py,sha256=l8DcRkA---CESVJgc7iTHLWIBAPGxWIfoem8qSU3lZQ,4972
76
- flwr/cli/utils.py,sha256=Y2VSSsgPeWMjTdezXfrDWvCwYIabmvnsRYXssDt_MBQ,11965
76
+ flwr/cli/utils.py,sha256=Ca2jnbzqGsQWAHV_ztrGi32DpE-lf0gR90DOL76bR6c,12346
77
77
  flwr/client/__init__.py,sha256=boIhKaK6I977zrILmoTutNx94x5jB0e6F1gnAjaRJnI,1250
78
78
  flwr/client/client.py,sha256=3HAchxvknKG9jYbB7swNyDj-e5vUWDuMKoLvbT7jCVM,7895
79
79
  flwr/client/client_app.py,sha256=zVhi-l3chAb06ozFsKwix3hU_RpOLjST13Ha50AVIPE,16918
@@ -84,7 +84,7 @@ flwr/client/grpc_adapter_client/__init__.py,sha256=RQWP5mFPROLHKgombiRvPXVWSoVrQ
84
84
  flwr/client/grpc_adapter_client/connection.py,sha256=JGv02EjSOAG1E5BRUD4lwXc1LLiYJ0OhInvp31qx1cU,4404
85
85
  flwr/client/grpc_rere_client/__init__.py,sha256=i7iS0Lt8B7q0E2L72e4F_YrKm6ClRKnd71PNA6PW2O0,752
86
86
  flwr/client/grpc_rere_client/client_interceptor.py,sha256=zFaVHw6AxeNO-7eCKKb-RxrPa7zbM5Z-2-1Efc4adQY,2451
87
- flwr/client/grpc_rere_client/connection.py,sha256=__WQgS02WgOXopDnuighOzWLBVQY-0YCGtVjFhLaTZQ,13603
87
+ flwr/client/grpc_rere_client/connection.py,sha256=3P7hyu79th3ZtMbBLgGLODuTzwiO4fV6HmtoekNMifE,13521
88
88
  flwr/client/grpc_rere_client/grpc_adapter.py,sha256=dLGB5GriszAmtgvuFGuz_F7rIwpzLfDxhJ7T3Un-Ce0,6694
89
89
  flwr/client/message_handler/__init__.py,sha256=0lyljDVqre3WljiZbPcwCCf8GiIaSVI_yo_ylEyPwSE,719
90
90
  flwr/client/message_handler/message_handler.py,sha256=X9SXX6et97Lw9_DGD93HKsEBGNjXClcFgc_5aLK0oiU,6541
@@ -98,7 +98,7 @@ flwr/client/mod/secure_aggregation/secaggplus_mod.py,sha256=aKqjZCrikF73y3E-7h40
98
98
  flwr/client/mod/utils.py,sha256=FUgD2TfcWqSeF6jUKZ4i6Ke56U4Nrv85AeVb93s6R9g,1201
99
99
  flwr/client/numpy_client.py,sha256=Qq6ghsIAop2slKqAfgiI5NiHJ4LIxGmrik3Ror4_XVc,9581
100
100
  flwr/client/rest_client/__init__.py,sha256=MBiuK62hj439m9rtwSwI184Hth6Tt5GbmpNMyl3zkZY,735
101
- flwr/client/rest_client/connection.py,sha256=nGWgvOEvXpGcpu6G2Uxz34qOnB1NutIVi4tDMgvvJ1E,15738
101
+ flwr/client/rest_client/connection.py,sha256=NsENBljtBCd6ToIVmAhBbVC6kTmtuArxb2LnE6-lIGY,15656
102
102
  flwr/client/run_info_store.py,sha256=MaJ3UQ-07hWtK67wnWu0zR29jrk0fsfgJX506dvEOfE,4042
103
103
  flwr/client/typing.py,sha256=Jw3rawDzI_-ZDcRmEQcs5gZModY7oeQlEeltYsdOhlU,1048
104
104
  flwr/clientapp/__init__.py,sha256=zGW4z49Ojzoi1hDiRC7kyhLjijUilc6fqHhtM_ATRVA,719
@@ -124,7 +124,7 @@ flwr/common/grpc.py,sha256=y70hUFvXkIf3l03xOhlb7qhS6W1UJZRSZqCdB0ir0v8,10381
124
124
  flwr/common/heartbeat.py,sha256=SyEpNDnmJ0lni0cWO67rcoJVKasCLmkNHm3dKLeNrLU,5749
125
125
  flwr/common/inflatable.py,sha256=GDL9oBKs16_yyVdlH6kBf493O5xll_h9V7XB5Mpx1Hc,9524
126
126
  flwr/common/inflatable_protobuf_utils.py,sha256=JtRqp-fV47goDM2y8JRQ7AmwwjeGaWexwoMWLcxX5gE,5056
127
- flwr/common/inflatable_utils.py,sha256=tYNrsdUWIw94p1oBPojg2wlD_-tnspWU2tbmIRafB6U,17401
127
+ flwr/common/inflatable_utils.py,sha256=OcNW8_CfHNuJhHekwRtnrDJZ4vbhujw2w7SG7Y0TuSI,18955
128
128
  flwr/common/logger.py,sha256=JbRf6E2vQxXzpDBq1T8IDUJo_usu3gjWEBPQ6uKcmdg,13049
129
129
  flwr/common/message.py,sha256=xAL7iZN5-n-xPQpgoSFvxNrzs8fmiiPfoU0DjNQEhRw,19953
130
130
  flwr/common/object_ref.py,sha256=p3SfTeqo3Aj16SkB-vsnNn01zswOPdGNBitcbRnqmUk,9134
@@ -164,8 +164,8 @@ flwr/compat/server/__init__.py,sha256=TGVSoOTuf5T5JHUVrK5wuorQF7L6Wvdem8B4uufvMJ
164
164
  flwr/compat/server/app.py,sha256=_lIe7Q4KUk-olq9PYBxIsO3UaOn6N92CWgWQ4hRcAZw,6490
165
165
  flwr/compat/simulation/__init__.py,sha256=MApGa-tysDDw34iSdxZ7TWOKtGJM-z3i8fIRJa0qbZ8,750
166
166
  flwr/proto/__init__.py,sha256=S3VbQzVwNC1P-3_9EdrXuwgptO-BVuuAe20Z_OUc1cQ,683
167
- flwr/proto/appio_pb2.py,sha256=wCmsFJphNn5VHV2ew6-Wz58OclbsNfiwkwbssX8Ojf0,3853
168
- flwr/proto/appio_pb2.pyi,sha256=_75EI4-BtZFW_tQcjLYhULmZPlFh1WJGVpL4vOROljU,7736
167
+ flwr/proto/appio_pb2.py,sha256=3Mwzq1d__hYMfhCEkERZ6XwCpcd328zB6jg-WH4IX8o,3326
168
+ flwr/proto/appio_pb2.pyi,sha256=PNPX6hu9J-pnVoFOULuQNjILR3xzdk0Q3cz10p2u2GU,6978
169
169
  flwr/proto/appio_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
170
170
  flwr/proto/appio_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
171
171
  flwr/proto/clientappio_pb2.py,sha256=3b87t4_AFgeDopgpYhhOVBrdh1HG74W2HC-y0C0q_eY,3349
@@ -184,8 +184,8 @@ flwr/proto/fab_pb2.py,sha256=2Nu0WaWxDZ8TbutMtctjdcGM7OtXiyP4kmCgg5o7Jjw,1627
184
184
  flwr/proto/fab_pb2.pyi,sha256=AMXpiDK0fo3nZWjxsC2E4otSaVjyQbU7iiWKrsSZavs,2395
185
185
  flwr/proto/fab_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
186
186
  flwr/proto/fab_pb2_grpc.pyi,sha256=ff2TSiLVnG6IVQcTGzb2DIH3XRSoAvAo_RMcvbMFyc0,76
187
- flwr/proto/fleet_pb2.py,sha256=GbPhzqW-yBYduHcmlgc3YiXOeOZ_ThT46QfE6ck7jak,5660
188
- flwr/proto/fleet_pb2.pyi,sha256=K6Qt5lb_wVekUbgNA5Y5ora4N4Ow9bclp2h85VKiZBU,8555
187
+ flwr/proto/fleet_pb2.py,sha256=VZO6c1Vom-zfdRHgkuHROvsFdt4MOA8-R6p1TT-QXRo,5150
188
+ flwr/proto/fleet_pb2.pyi,sha256=0aKdrVaugxVI1ySWiy86aan1cjw9RJkDv1mTcMPyW4I,7797
189
189
  flwr/proto/fleet_pb2_grpc.py,sha256=NmzrDYxyM3MQNh3vwYczQNuFimZz3prU6ke3E-fKk_g,17539
190
190
  flwr/proto/fleet_pb2_grpc.pyi,sha256=PDERhzOrBCMAytTLS65Qck8A45bTIYni7Lotq6_I0sM,4721
191
191
  flwr/proto/grpcadapter_pb2.py,sha256=PJ8DtfeV29g_y4Z3aNZlSZocLqSxeLmTsYCdOZDYCiE,1843
@@ -242,7 +242,7 @@ flwr/server/criterion.py,sha256=G4e-6B48Pc7d5rmGVUpIzNKb6UF88O3VmTRuUltgjzM,1061
242
242
  flwr/server/fleet_event_log_interceptor.py,sha256=gtVPr2yQp3U2GnabIdFnuok18VerEcidKj1lKmDYGoY,3950
243
243
  flwr/server/grid/__init__.py,sha256=aWZHezoR2UGMJISB_gPMCm2N_2GSbm97A3lAp7ruhRQ,888
244
244
  flwr/server/grid/grid.py,sha256=naGCYt5J6dnmUvrcGkdNyKPe3MBd-0awGm1ALmgahqY,6625
245
- flwr/server/grid/grpc_grid.py,sha256=anUMektTtlAWcvu6wf7dsoiuBF5OiXfKuginHx0RWT0,15292
245
+ flwr/server/grid/grpc_grid.py,sha256=B7_F04Nh0EWX_xXptfCNxFLGS3enzWj2a4oMU0fyACs,15298
246
246
  flwr/server/grid/inmemory_grid.py,sha256=RjejYT-d-hHuTs1KSs_5wvOdAWKLus8w5_UAcnGt4iw,6168
247
247
  flwr/server/history.py,sha256=cCkFhBN4GoHsYYNk5GG1Y089eKJh2DH_ZJbYPwLaGyk,5026
248
248
  flwr/server/run_serverapp.py,sha256=v0p6jXj2dFxlRUdoEeF1mnaFd9XRQi6dZCflPY6d3qI,2063
@@ -250,7 +250,7 @@ flwr/server/server.py,sha256=39m4FSN2T-uVA-no9nstN0eWW0co-IUUAIMmpd3V7Jc,17893
250
250
  flwr/server/server_app.py,sha256=8uagoZX-3CY3tazPqkIV9jY-cN0YrRRrDmVe23o0AV0,9515
251
251
  flwr/server/server_config.py,sha256=e_6ddh0riwOJsdNn2BFev344uMWfDk9n7dyjNpPgm1w,1349
252
252
  flwr/server/serverapp/__init__.py,sha256=xcC0T_MQSMS9cicUzUUpMNCOsF2d8Oh_8jvnoBLuZvo,800
253
- flwr/server/serverapp/app.py,sha256=GetSKAsDlNlJFazregZ_Vu63SIIL9KrZXEvSjND7qA0,9407
253
+ flwr/server/serverapp/app.py,sha256=k0wAdZiMAEz7WsUigxv038I5Eel3vXwuSqGGj2HtX3c,9524
254
254
  flwr/server/serverapp_components.py,sha256=dfSqmrsVy3arKXpl3ZIBQWdV8rehfIms8aJooyzdmEM,2118
255
255
  flwr/server/strategy/__init__.py,sha256=HhsSWMWaC7oCb2g7Kqn1MBKdrfvgi8VxACy9ZL706Q0,2836
256
256
  flwr/server/strategy/aggregate.py,sha256=smlKKy-uFUuuFR12vlclucnwSQWRz78R79-Km4RWqbw,13978
@@ -322,13 +322,13 @@ flwr/server/workflow/secure_aggregation/secagg_workflow.py,sha256=b_pKk7gmbahwyj
322
322
  flwr/server/workflow/secure_aggregation/secaggplus_workflow.py,sha256=DkayCsnlAya6Y2PZsueLgoUCMRtV-GbnW08RfWx_SXM,29460
323
323
  flwr/serverapp/__init__.py,sha256=HPvC_ZvMS7GCM7ALVrG_Wwm4bSDr4DZETeC561v3T9w,719
324
324
  flwr/simulation/__init__.py,sha256=Gg6OsP1Z-ixc3-xxzvl7j7rz2Fijy9rzyEPpxgAQCeM,1556
325
- flwr/simulation/app.py,sha256=Uy3lPwAvfZECkWPLcC0oDXTwY14e4ou8itIcBltjmWE,10437
325
+ flwr/simulation/app.py,sha256=hP7vP29qZOA_3dipzUdmkRXP25eS8RlF1MT-S09t3oc,10625
326
326
  flwr/simulation/legacy_app.py,sha256=nMISQqW0otJL1-2Kfd94O6BLlGS2IEmEPKTM2WGKrIs,15861
327
327
  flwr/simulation/ray_transport/__init__.py,sha256=ogd-0AMv2U-wBZ1r3sHWaDIOIrVqr88Xi6C8o4Dviy0,734
328
328
  flwr/simulation/ray_transport/ray_actor.py,sha256=JN3xTqFIr5Z750k92CcA_uavzOHhSWDwE2WCaecvpks,19147
329
329
  flwr/simulation/ray_transport/ray_client_proxy.py,sha256=2kVUDrJe2ViOJEuB0v_Xb3XUwK9yKNwDwrYQXTJDdco,7506
330
330
  flwr/simulation/ray_transport/utils.py,sha256=KrexpWYCF-dAF3UHc9yDbPQWO-ahMT-BbD8nURLgiHk,2393
331
- flwr/simulation/run_simulation.py,sha256=Nvw_6hI71aE2nU95_tt1F9VSo3OJWrvA97e3XZuqE4k,20310
331
+ flwr/simulation/run_simulation.py,sha256=-sp3dNZcp7MCAH0BlmZpVcFAGvozRdYXRdDYcH_2Zxk,20838
332
332
  flwr/simulation/simulationio_connection.py,sha256=mzS1C6EEREwQDPceDo30anAasmTDLb9qqV2tXlBhOUA,3494
333
333
  flwr/supercore/__init__.py,sha256=pqkFoow_E6UhbBlhmoD1gmTH-33yJRhBsIZqxRPFZ7U,755
334
334
  flwr/supercore/ffs/__init__.py,sha256=U3KXwG_SplEvchat27K0LYPoPHzh-cwwT_NHsGlYMt8,908
@@ -343,7 +343,7 @@ flwr/supercore/object_store/__init__.py,sha256=cdfPAmjINY6iOp8oI_LdcVh2simg469Mk
343
343
  flwr/supercore/object_store/in_memory_object_store.py,sha256=CGY43syxDGrUPcdOzRH3hNrfeqmoTOY_wjo3qaAHuNk,9612
344
344
  flwr/supercore/object_store/object_store.py,sha256=J-rI3X7ET-F6dqOyM-UfHKCCQtPJ_EnYW62H_1txts0,5252
345
345
  flwr/supercore/object_store/object_store_factory.py,sha256=QVwE2ywi7vsj2iKfvWWnNw3N_I7Rz91NUt2RpcbJ7iM,1527
346
- flwr/supercore/object_store/utils.py,sha256=-WwBa6ejMNm9ahmNZP39IHutS0cwingmeqCoxTmATQM,1845
346
+ flwr/supercore/object_store/utils.py,sha256=DcPbrb9PenloAPoQRiKiXX9DrDfpXcSyY0cZpgn4PeQ,1680
347
347
  flwr/supercore/scheduler/__init__.py,sha256=E4GviiNJoZKz1dOao8ZGRHExsiM23GtOrkpMrTHy3n8,787
348
348
  flwr/supercore/scheduler/plugin.py,sha256=kIv0JUrHP-ghrcGT-pbporL9A2mUz8PxASw6KgxCRv8,2460
349
349
  flwr/supercore/utils.py,sha256=ebuHMbeA8eXisX0oMPqBK3hk7uVnIE_yiqWVz8YbkpQ,1324
@@ -367,14 +367,14 @@ flwr/supernode/nodestate/in_memory_nodestate.py,sha256=LF3AbaW0bcZHY5yKWwUJSU2RZ
367
367
  flwr/supernode/nodestate/nodestate.py,sha256=kkGFxYnLIwT4-UmlPnf6HvAUpPey2urUNrweGybAIY4,6398
368
368
  flwr/supernode/nodestate/nodestate_factory.py,sha256=UYTDCcwK_baHUmkzkJDxL0UEqvtTfOMlQRrROMCd0Xo,1430
369
369
  flwr/supernode/runtime/__init__.py,sha256=JQdqd2EMTn-ORMeTvewYYh52ls0YKP68jrps1qioxu4,718
370
- flwr/supernode/runtime/run_clientapp.py,sha256=woAO8rXclt5eZeNHokhBChgxMf-TAzqWnHCkoiSsLVs,10765
370
+ flwr/supernode/runtime/run_clientapp.py,sha256=0ysvN07xPDysFVViu2QyRR7BUI4Hv8ZcacmaADagvdQ,10701
371
371
  flwr/supernode/scheduler/__init__.py,sha256=nQLi5ROVCMz8ii_WsZn4MAqKHXI40Eb3tq5-9zZbmpg,850
372
372
  flwr/supernode/scheduler/simple_clientapp_scheduler_plugin.py,sha256=nnNuKhelrAEGCnWZ3aAkqJrhPu7xlVQ-oO1Eih9shTU,2000
373
373
  flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca8gxdEo,717
374
374
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
375
375
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=ClPoKco7Tjj_cxRPhZlQSrOvcGa8sJwGs26LUNZnI3Y,10608
376
376
  flwr/supernode/start_client_internal.py,sha256=VC7rV4QaX5Vk4Lw0DDodhwsKvGiHbr0mqrV0H77h1UI,21999
377
- flwr_nightly-1.20.0.dev20250725.dist-info/METADATA,sha256=P-wJ9RAHghetpRLtoTFCq-XODbPtE2D1xuWeYhFakuw,15966
378
- flwr_nightly-1.20.0.dev20250725.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
379
- flwr_nightly-1.20.0.dev20250725.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
380
- flwr_nightly-1.20.0.dev20250725.dist-info/RECORD,,
377
+ flwr_nightly-1.21.0.dev20250729.dist-info/METADATA,sha256=GHFlQXdDz-wfpA4HvkHCNFyBN-mRXY1F85QgYuKUE_c,15966
378
+ flwr_nightly-1.21.0.dev20250729.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
379
+ flwr_nightly-1.21.0.dev20250729.dist-info/entry_points.txt,sha256=jNpDXGBGgs21RqUxelF_jwGaxtqFwm-MQyfz-ZqSjrA,367
380
+ flwr_nightly-1.21.0.dev20250729.dist-info/RECORD,,