flwr 1.16.0__py3-none-any.whl → 1.18.0__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 (248) hide show
  1. flwr/__init__.py +1 -1
  2. flwr/cli/__init__.py +1 -1
  3. flwr/cli/app.py +21 -2
  4. flwr/cli/build.py +1 -1
  5. flwr/cli/cli_user_auth_interceptor.py +1 -1
  6. flwr/cli/config_utils.py +53 -17
  7. flwr/cli/example.py +1 -1
  8. flwr/cli/install.py +1 -1
  9. flwr/cli/log.py +1 -1
  10. flwr/cli/login/__init__.py +1 -1
  11. flwr/cli/login/login.py +12 -1
  12. flwr/cli/ls.py +1 -1
  13. flwr/cli/new/__init__.py +1 -1
  14. flwr/cli/new/new.py +4 -4
  15. flwr/cli/new/templates/__init__.py +1 -1
  16. flwr/cli/new/templates/app/__init__.py +1 -1
  17. flwr/cli/new/templates/app/code/__init__.py +1 -1
  18. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
  19. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +5 -5
  20. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
  21. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
  22. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
  23. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  24. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  25. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  26. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  27. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  28. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  29. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  30. flwr/cli/run/__init__.py +1 -1
  31. flwr/cli/run/run.py +6 -10
  32. flwr/cli/stop.py +1 -1
  33. flwr/cli/utils.py +11 -12
  34. flwr/client/__init__.py +1 -1
  35. flwr/client/app.py +58 -56
  36. flwr/client/client.py +1 -1
  37. flwr/client/client_app.py +231 -166
  38. flwr/client/clientapp/__init__.py +1 -1
  39. flwr/client/clientapp/app.py +3 -3
  40. flwr/client/clientapp/clientappio_servicer.py +1 -1
  41. flwr/client/clientapp/utils.py +1 -1
  42. flwr/client/dpfedavg_numpy_client.py +1 -1
  43. flwr/client/grpc_adapter_client/__init__.py +1 -1
  44. flwr/client/grpc_adapter_client/connection.py +1 -1
  45. flwr/client/grpc_client/__init__.py +1 -1
  46. flwr/client/grpc_client/connection.py +37 -34
  47. flwr/client/grpc_rere_client/__init__.py +1 -1
  48. flwr/client/grpc_rere_client/client_interceptor.py +1 -1
  49. flwr/client/grpc_rere_client/connection.py +1 -1
  50. flwr/client/grpc_rere_client/grpc_adapter.py +1 -1
  51. flwr/client/heartbeat.py +1 -1
  52. flwr/client/message_handler/__init__.py +1 -1
  53. flwr/client/message_handler/message_handler.py +28 -28
  54. flwr/client/mod/__init__.py +3 -3
  55. flwr/client/mod/centraldp_mods.py +8 -8
  56. flwr/client/mod/comms_mods.py +17 -23
  57. flwr/client/mod/localdp_mod.py +10 -10
  58. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  59. flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
  60. flwr/client/mod/secure_aggregation/secaggplus_mod.py +32 -32
  61. flwr/client/mod/utils.py +1 -1
  62. flwr/client/nodestate/__init__.py +1 -1
  63. flwr/client/nodestate/in_memory_nodestate.py +1 -1
  64. flwr/client/nodestate/nodestate.py +1 -1
  65. flwr/client/nodestate/nodestate_factory.py +1 -1
  66. flwr/client/numpy_client.py +1 -1
  67. flwr/client/rest_client/__init__.py +1 -1
  68. flwr/client/rest_client/connection.py +1 -1
  69. flwr/client/run_info_store.py +3 -3
  70. flwr/client/supernode/__init__.py +1 -1
  71. flwr/client/supernode/app.py +1 -1
  72. flwr/client/typing.py +1 -1
  73. flwr/common/__init__.py +13 -5
  74. flwr/common/address.py +1 -1
  75. flwr/common/args.py +1 -1
  76. flwr/common/auth_plugin/__init__.py +1 -1
  77. flwr/common/auth_plugin/auth_plugin.py +1 -1
  78. flwr/common/config.py +5 -5
  79. flwr/common/constant.py +7 -7
  80. flwr/common/context.py +5 -5
  81. flwr/common/date.py +1 -1
  82. flwr/common/differential_privacy.py +1 -1
  83. flwr/common/differential_privacy_constants.py +1 -1
  84. flwr/common/dp.py +1 -1
  85. flwr/common/event_log_plugin/event_log_plugin.py +3 -3
  86. flwr/common/exit/exit.py +6 -6
  87. flwr/common/exit_handlers.py +1 -1
  88. flwr/common/grpc.py +1 -1
  89. flwr/common/logger.py +3 -3
  90. flwr/common/message.py +344 -102
  91. flwr/common/object_ref.py +1 -1
  92. flwr/common/parameter.py +1 -1
  93. flwr/common/pyproject.py +1 -1
  94. flwr/common/record/__init__.py +9 -5
  95. flwr/common/record/arrayrecord.py +626 -0
  96. flwr/common/record/{configsrecord.py → configrecord.py} +83 -37
  97. flwr/common/record/conversion_utils.py +2 -2
  98. flwr/common/record/{metricsrecord.py → metricrecord.py} +90 -44
  99. flwr/common/record/recorddict.py +337 -0
  100. flwr/common/record/typeddict.py +1 -1
  101. flwr/common/recorddict_compat.py +410 -0
  102. flwr/common/retry_invoker.py +10 -10
  103. flwr/common/secure_aggregation/__init__.py +1 -1
  104. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  105. flwr/common/secure_aggregation/crypto/shamir.py +52 -30
  106. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
  107. flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
  108. flwr/common/secure_aggregation/quantization.py +1 -1
  109. flwr/common/secure_aggregation/secaggplus_constants.py +2 -2
  110. flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
  111. flwr/common/serde.py +67 -72
  112. flwr/common/telemetry.py +2 -2
  113. flwr/common/typing.py +9 -9
  114. flwr/common/version.py +1 -1
  115. flwr/proto/__init__.py +1 -1
  116. flwr/proto/exec_pb2.py +3 -3
  117. flwr/proto/exec_pb2.pyi +3 -3
  118. flwr/proto/message_pb2.py +12 -12
  119. flwr/proto/message_pb2.pyi +9 -9
  120. flwr/proto/recorddict_pb2.py +70 -0
  121. flwr/proto/{recordset_pb2.pyi → recorddict_pb2.pyi} +35 -35
  122. flwr/proto/run_pb2.py +31 -31
  123. flwr/proto/run_pb2.pyi +3 -3
  124. flwr/server/__init__.py +4 -2
  125. flwr/server/app.py +67 -12
  126. flwr/server/client_manager.py +1 -1
  127. flwr/server/client_proxy.py +1 -1
  128. flwr/server/compat/__init__.py +3 -3
  129. flwr/server/compat/app.py +12 -12
  130. flwr/server/compat/app_utils.py +17 -17
  131. flwr/server/compat/{driver_client_proxy.py → grid_client_proxy.py} +39 -39
  132. flwr/server/compat/legacy_context.py +1 -1
  133. flwr/server/criterion.py +1 -1
  134. flwr/server/fleet_event_log_interceptor.py +94 -0
  135. flwr/server/{driver → grid}/__init__.py +8 -7
  136. flwr/server/{driver/driver.py → grid/grid.py} +48 -19
  137. flwr/server/{driver/grpc_driver.py → grid/grpc_grid.py} +87 -64
  138. flwr/server/{driver/inmemory_driver.py → grid/inmemory_grid.py} +24 -34
  139. flwr/server/history.py +1 -1
  140. flwr/server/run_serverapp.py +5 -5
  141. flwr/server/server.py +1 -1
  142. flwr/server/server_app.py +98 -71
  143. flwr/server/server_config.py +1 -1
  144. flwr/server/serverapp/__init__.py +1 -1
  145. flwr/server/serverapp/app.py +11 -11
  146. flwr/server/serverapp_components.py +1 -1
  147. flwr/server/strategy/__init__.py +1 -1
  148. flwr/server/strategy/aggregate.py +1 -1
  149. flwr/server/strategy/bulyan.py +2 -2
  150. flwr/server/strategy/dp_adaptive_clipping.py +17 -17
  151. flwr/server/strategy/dp_fixed_clipping.py +17 -17
  152. flwr/server/strategy/dpfedavg_adaptive.py +1 -1
  153. flwr/server/strategy/dpfedavg_fixed.py +1 -1
  154. flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
  155. flwr/server/strategy/fedadagrad.py +1 -1
  156. flwr/server/strategy/fedadam.py +1 -1
  157. flwr/server/strategy/fedavg.py +1 -1
  158. flwr/server/strategy/fedavg_android.py +1 -1
  159. flwr/server/strategy/fedavgm.py +1 -1
  160. flwr/server/strategy/fedmedian.py +1 -1
  161. flwr/server/strategy/fedopt.py +1 -1
  162. flwr/server/strategy/fedprox.py +1 -1
  163. flwr/server/strategy/fedtrimmedavg.py +1 -1
  164. flwr/server/strategy/fedxgb_bagging.py +1 -1
  165. flwr/server/strategy/fedxgb_cyclic.py +1 -1
  166. flwr/server/strategy/fedxgb_nn_avg.py +3 -2
  167. flwr/server/strategy/fedyogi.py +1 -1
  168. flwr/server/strategy/krum.py +1 -1
  169. flwr/server/strategy/qfedavg.py +1 -1
  170. flwr/server/strategy/strategy.py +1 -1
  171. flwr/server/superlink/__init__.py +1 -1
  172. flwr/server/superlink/ffs/__init__.py +1 -1
  173. flwr/server/superlink/ffs/disk_ffs.py +1 -1
  174. flwr/server/superlink/ffs/ffs.py +1 -1
  175. flwr/server/superlink/ffs/ffs_factory.py +1 -1
  176. flwr/server/superlink/fleet/__init__.py +1 -1
  177. flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
  178. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +1 -1
  179. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  180. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
  181. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
  182. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  183. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
  184. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  185. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +1 -1
  186. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
  187. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  188. flwr/server/superlink/fleet/message_handler/message_handler.py +1 -1
  189. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  190. flwr/server/superlink/fleet/rest_rere/rest_api.py +1 -1
  191. flwr/server/superlink/fleet/vce/__init__.py +1 -1
  192. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
  193. flwr/server/superlink/fleet/vce/backend/backend.py +3 -3
  194. flwr/server/superlink/fleet/vce/backend/raybackend.py +3 -3
  195. flwr/server/superlink/fleet/vce/vce_api.py +2 -4
  196. flwr/server/superlink/linkstate/__init__.py +1 -1
  197. flwr/server/superlink/linkstate/in_memory_linkstate.py +34 -9
  198. flwr/server/superlink/linkstate/linkstate.py +5 -5
  199. flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
  200. flwr/server/superlink/linkstate/sqlite_linkstate.py +62 -28
  201. flwr/server/superlink/linkstate/utils.py +94 -28
  202. flwr/server/superlink/{driver → serverappio}/__init__.py +1 -1
  203. flwr/server/superlink/{driver → serverappio}/serverappio_grpc.py +1 -1
  204. flwr/server/superlink/{driver → serverappio}/serverappio_servicer.py +4 -4
  205. flwr/server/superlink/simulation/__init__.py +1 -1
  206. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  207. flwr/server/superlink/simulation/simulationio_servicer.py +3 -3
  208. flwr/server/superlink/utils.py +1 -1
  209. flwr/server/typing.py +4 -4
  210. flwr/server/utils/__init__.py +1 -1
  211. flwr/server/utils/tensorboard.py +1 -1
  212. flwr/server/utils/validator.py +5 -5
  213. flwr/server/workflow/__init__.py +1 -1
  214. flwr/server/workflow/constant.py +1 -1
  215. flwr/server/workflow/default_workflows.py +49 -58
  216. flwr/server/workflow/secure_aggregation/__init__.py +1 -1
  217. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
  218. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +49 -51
  219. flwr/simulation/__init__.py +1 -1
  220. flwr/simulation/app.py +3 -3
  221. flwr/simulation/legacy_app.py +1 -1
  222. flwr/simulation/ray_transport/__init__.py +1 -1
  223. flwr/simulation/ray_transport/ray_actor.py +5 -3
  224. flwr/simulation/ray_transport/ray_client_proxy.py +35 -33
  225. flwr/simulation/ray_transport/utils.py +1 -1
  226. flwr/simulation/run_simulation.py +17 -17
  227. flwr/simulation/simulationio_connection.py +1 -1
  228. flwr/superexec/__init__.py +1 -1
  229. flwr/superexec/app.py +1 -1
  230. flwr/superexec/deployment.py +5 -5
  231. flwr/superexec/exec_event_log_interceptor.py +135 -0
  232. flwr/superexec/exec_grpc.py +11 -5
  233. flwr/superexec/exec_servicer.py +3 -3
  234. flwr/superexec/exec_user_auth_interceptor.py +19 -3
  235. flwr/superexec/executor.py +4 -4
  236. flwr/superexec/simulation.py +4 -4
  237. {flwr-1.16.0.dist-info → flwr-1.18.0.dist-info}/METADATA +3 -3
  238. flwr-1.18.0.dist-info/RECORD +332 -0
  239. flwr/common/record/parametersrecord.py +0 -339
  240. flwr/common/record/recordset.py +0 -209
  241. flwr/common/recordset_compat.py +0 -418
  242. flwr/proto/recordset_pb2.py +0 -70
  243. flwr-1.16.0.dist-info/LICENSE +0 -202
  244. flwr-1.16.0.dist-info/RECORD +0 -331
  245. /flwr/proto/{recordset_pb2_grpc.py → recorddict_pb2_grpc.py} +0 -0
  246. /flwr/proto/{recordset_pb2_grpc.pyi → recorddict_pb2_grpc.pyi} +0 -0
  247. {flwr-1.16.0.dist-info → flwr-1.18.0.dist-info}/WHEEL +0 -0
  248. {flwr-1.16.0.dist-info → flwr-1.18.0.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -96,18 +96,18 @@ def start_grpc_server( # pylint: disable=too-many-arguments,R0917
96
96
 
97
97
  Examples
98
98
  --------
99
- Starting a SSL-enabled server.
100
-
101
- >>> from pathlib import Path
102
- >>> start_grpc_server(
103
- >>> client_manager=ClientManager(),
104
- >>> server_address="localhost:8080",
105
- >>> certificates=(
106
- >>> Path("/crts/root.pem").read_bytes(),
107
- >>> Path("/crts/localhost.crt").read_bytes(),
108
- >>> Path("/crts/localhost.key").read_bytes(),
109
- >>> ),
110
- >>> )
99
+ Starting a TLS-enabled server::
100
+
101
+ from pathlib import Path
102
+ start_grpc_server(
103
+ client_manager=ClientManager(),
104
+ server_address="localhost:8080",
105
+ certificates=(
106
+ Path("/crts/root.pem").read_bytes(),
107
+ Path("/crts/localhost.crt").read_bytes(),
108
+ Path("/crts/localhost.key").read_bytes(),
109
+ ),
110
+ )
111
111
  """
112
112
  servicer = FlowerServiceServicer(client_manager)
113
113
  add_servicer_to_server_fn = add_FlowerServiceServicer_to_server
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -21,9 +21,9 @@ from typing import Callable
21
21
  from flwr.client.client_app import ClientApp
22
22
  from flwr.common.context import Context
23
23
  from flwr.common.message import Message
24
- from flwr.common.typing import ConfigsRecordValues
24
+ from flwr.common.typing import ConfigRecordValues
25
25
 
26
- BackendConfig = dict[str, dict[str, ConfigsRecordValues]]
26
+ BackendConfig = dict[str, dict[str, ConfigRecordValues]]
27
27
 
28
28
 
29
29
  class Backend(ABC):
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@ from flwr.common.constant import PARTITION_ID_KEY
26
26
  from flwr.common.context import Context
27
27
  from flwr.common.logger import log
28
28
  from flwr.common.message import Message
29
- from flwr.common.typing import ConfigsRecordValues
29
+ from flwr.common.typing import ConfigRecordValues
30
30
  from flwr.simulation.ray_transport.ray_actor import BasicActorPool, ClientAppActor
31
31
  from flwr.simulation.ray_transport.utils import enable_tf_gpu_growth
32
32
 
@@ -104,7 +104,7 @@ class RayBackend(Backend):
104
104
  if not ray.is_initialized():
105
105
  ray_init_args: dict[
106
106
  str,
107
- ConfigsRecordValues,
107
+ ConfigRecordValues,
108
108
  ] = {}
109
109
 
110
110
  if backend_config.get(self.init_args_key):
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -130,9 +130,7 @@ def worker(
130
130
  e_code = ErrorCode.UNKNOWN
131
131
 
132
132
  reason = str(type(ex)) + ":<'" + str(ex) + "'>"
133
- out_mssg = message.create_error_reply(
134
- error=Error(code=e_code, reason=reason)
135
- )
133
+ out_mssg = Message(Error(code=e_code, reason=reason), reply_to=message)
136
134
 
137
135
  finally:
138
136
  if out_mssg:
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -27,16 +27,18 @@ from flwr.common import Context, Message, log, now
27
27
  from flwr.common.constant import (
28
28
  MESSAGE_TTL_TOLERANCE,
29
29
  NODE_ID_NUM_BYTES,
30
+ PING_PATIENCE,
30
31
  RUN_ID_NUM_BYTES,
31
32
  SUPERLINK_NODE_ID,
32
33
  Status,
33
34
  )
34
- from flwr.common.record import ConfigsRecord
35
+ from flwr.common.record import ConfigRecord
35
36
  from flwr.common.typing import Run, RunStatus, UserConfig
36
37
  from flwr.server.superlink.linkstate.linkstate import LinkState
37
38
  from flwr.server.utils import validate_message
38
39
 
39
40
  from .utils import (
41
+ check_node_availability_for_in_message,
40
42
  generate_rand_int_from_bytes,
41
43
  has_valid_sub_status,
42
44
  is_valid_transition,
@@ -67,7 +69,7 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
67
69
  # Map run_id to RunRecord
68
70
  self.run_ids: dict[int, RunRecord] = {}
69
71
  self.contexts: dict[int, Context] = {}
70
- self.federation_options: dict[int, ConfigsRecord] = {}
72
+ self.federation_options: dict[int, ConfigRecord] = {}
71
73
  self.message_ins_store: dict[UUID, Message] = {}
72
74
  self.message_res_store: dict[UUID, Message] = {}
73
75
  self.message_ins_id_to_message_res_id: dict[UUID, UUID] = {}
@@ -156,7 +158,7 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
156
158
  res_metadata = message.metadata
157
159
  with self.lock:
158
160
  # Check if the Message it is replying to exists and is valid
159
- msg_ins_id = res_metadata.reply_to_message
161
+ msg_ins_id = res_metadata.reply_to_message_id
160
162
  msg_ins = self.message_ins_store.get(UUID(msg_ins_id))
161
163
 
162
164
  # Ensure that dst_node_id of original Message matches the src_node_id of
@@ -232,13 +234,28 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
232
234
  with self.lock:
233
235
  current = time.time()
234
236
 
235
- # Verify Messge IDs
237
+ # Verify Message IDs
236
238
  ret = verify_message_ids(
237
239
  inquired_message_ids=message_ids,
238
240
  found_message_ins_dict=self.message_ins_store,
239
241
  current_time=current,
240
242
  )
241
243
 
244
+ # Check node availability
245
+ dst_node_ids = {
246
+ self.message_ins_store[message_id].metadata.dst_node_id
247
+ for message_id in message_ids
248
+ }
249
+ tmp_ret_dict = check_node_availability_for_in_message(
250
+ inquired_in_message_ids=message_ids,
251
+ found_in_message_dict=self.message_ins_store,
252
+ node_id_to_online_until={
253
+ node_id: self.node_ids[node_id][0] for node_id in dst_node_ids
254
+ },
255
+ current_time=current,
256
+ )
257
+ ret.update(tmp_ret_dict)
258
+
242
259
  # Find all reply Messages
243
260
  message_res_found: list[Message] = []
244
261
  for message_id in message_ids:
@@ -317,6 +334,7 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
317
334
  log(ERROR, "Unexpected node registration failure.")
318
335
  return 0
319
336
 
337
+ # Mark the node online util time.time() + ping_interval
320
338
  self.node_ids[node_id] = (time.time() + ping_interval, ping_interval)
321
339
  return node_id
322
340
 
@@ -381,7 +399,7 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
381
399
  fab_version: Optional[str],
382
400
  fab_hash: Optional[str],
383
401
  override_config: UserConfig,
384
- federation_options: ConfigsRecord,
402
+ federation_options: ConfigRecord,
385
403
  ) -> int:
386
404
  """Create a new run for the specified `fab_hash`."""
387
405
  # Sample a random int64 as run_id
@@ -510,7 +528,7 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
510
528
 
511
529
  return pending_run_id
512
530
 
513
- def get_federation_options(self, run_id: int) -> Optional[ConfigsRecord]:
531
+ def get_federation_options(self, run_id: int) -> Optional[ConfigRecord]:
514
532
  """Retrieve the federation options for the specified `run_id`."""
515
533
  with self.lock:
516
534
  if run_id not in self.run_ids:
@@ -519,10 +537,17 @@ class InMemoryLinkState(LinkState): # pylint: disable=R0902,R0904
519
537
  return self.federation_options[run_id]
520
538
 
521
539
  def acknowledge_ping(self, node_id: int, ping_interval: float) -> bool:
522
- """Acknowledge a ping received from a node, serving as a heartbeat."""
540
+ """Acknowledge a ping received from a node, serving as a heartbeat.
541
+
542
+ It allows for one missed ping (in a PING_PATIENCE * ping_interval) before
543
+ marking the node as offline, where PING_PATIENCE = 2 in default.
544
+ """
523
545
  with self.lock:
524
546
  if node_id in self.node_ids:
525
- self.node_ids[node_id] = (time.time() + ping_interval, ping_interval)
547
+ self.node_ids[node_id] = (
548
+ time.time() + PING_PATIENCE * ping_interval,
549
+ ping_interval,
550
+ )
526
551
  return True
527
552
  return False
528
553
 
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ from typing import Optional
20
20
  from uuid import UUID
21
21
 
22
22
  from flwr.common import Context, Message
23
- from flwr.common.record import ConfigsRecord
23
+ from flwr.common.record import ConfigRecord
24
24
  from flwr.common.typing import Run, RunStatus, UserConfig
25
25
 
26
26
 
@@ -164,7 +164,7 @@ class LinkState(abc.ABC): # pylint: disable=R0904
164
164
  fab_version: Optional[str],
165
165
  fab_hash: Optional[str],
166
166
  override_config: UserConfig,
167
- federation_options: ConfigsRecord,
167
+ federation_options: ConfigRecord,
168
168
  ) -> int:
169
169
  """Create a new run for the specified `fab_hash`."""
170
170
 
@@ -236,7 +236,7 @@ class LinkState(abc.ABC): # pylint: disable=R0904
236
236
  """
237
237
 
238
238
  @abc.abstractmethod
239
- def get_federation_options(self, run_id: int) -> Optional[ConfigsRecord]:
239
+ def get_federation_options(self, run_id: int) -> Optional[ConfigRecord]:
240
240
  """Retrieve the federation options for the specified `run_id`.
241
241
 
242
242
  Parameters
@@ -246,7 +246,7 @@ class LinkState(abc.ABC): # pylint: disable=R0904
246
246
 
247
247
  Returns
248
248
  -------
249
- Optional[ConfigsRecord]
249
+ Optional[ConfigRecord]
250
250
  The federation options for the run if it exists; None otherwise.
251
251
  """
252
252
 
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -30,30 +30,33 @@ from flwr.common import Context, Message, Metadata, log, now
30
30
  from flwr.common.constant import (
31
31
  MESSAGE_TTL_TOLERANCE,
32
32
  NODE_ID_NUM_BYTES,
33
+ PING_PATIENCE,
33
34
  RUN_ID_NUM_BYTES,
34
35
  SUPERLINK_NODE_ID,
35
36
  Status,
36
37
  )
37
- from flwr.common.record import ConfigsRecord
38
+ from flwr.common.message import make_message
39
+ from flwr.common.record import ConfigRecord
38
40
  from flwr.common.serde import (
39
41
  error_from_proto,
40
42
  error_to_proto,
41
- recordset_from_proto,
42
- recordset_to_proto,
43
+ recorddict_from_proto,
44
+ recorddict_to_proto,
43
45
  )
44
46
  from flwr.common.typing import Run, RunStatus, UserConfig
45
47
 
46
48
  # pylint: disable=E0611
47
49
  from flwr.proto.error_pb2 import Error as ProtoError
48
- from flwr.proto.recordset_pb2 import RecordSet as ProtoRecordSet
50
+ from flwr.proto.recorddict_pb2 import RecordDict as ProtoRecordDict
49
51
 
50
52
  # pylint: enable=E0611
51
53
  from flwr.server.utils.validator import validate_message
52
54
 
53
55
  from .linkstate import LinkState
54
56
  from .utils import (
55
- configsrecord_from_bytes,
56
- configsrecord_to_bytes,
57
+ check_node_availability_for_in_message,
58
+ configrecord_from_bytes,
59
+ configrecord_to_bytes,
57
60
  context_from_bytes,
58
61
  context_to_bytes,
59
62
  convert_sint64_to_uint64,
@@ -129,7 +132,7 @@ CREATE TABLE IF NOT EXISTS message_ins(
129
132
  run_id INTEGER,
130
133
  src_node_id INTEGER,
131
134
  dst_node_id INTEGER,
132
- reply_to_message TEXT,
135
+ reply_to_message_id TEXT,
133
136
  created_at REAL,
134
137
  delivered_at TEXT,
135
138
  ttl REAL,
@@ -148,7 +151,7 @@ CREATE TABLE IF NOT EXISTS message_res(
148
151
  run_id INTEGER,
149
152
  src_node_id INTEGER,
150
153
  dst_node_id INTEGER,
151
- reply_to_message TEXT,
154
+ reply_to_message_id TEXT,
152
155
  created_at REAL,
153
156
  delivered_at TEXT,
154
157
  ttl REAL,
@@ -371,7 +374,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
371
374
  return None
372
375
 
373
376
  res_metadata = message.metadata
374
- msg_ins_id = res_metadata.reply_to_message
377
+ msg_ins_id = res_metadata.reply_to_message_id
375
378
  msg_ins = self.get_valid_message_ins(msg_ins_id)
376
379
  if msg_ins is None:
377
380
  log(
@@ -442,6 +445,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
442
445
 
443
446
  def get_message_res(self, message_ids: set[UUID]) -> list[Message]:
444
447
  """Get reply Messages for the given Message IDs."""
448
+ # pylint: disable-msg=too-many-locals
445
449
  ret: dict[UUID, Message] = {}
446
450
 
447
451
  # Verify Message IDs
@@ -465,11 +469,34 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
465
469
  current_time=current,
466
470
  )
467
471
 
472
+ # Check node availability
473
+ dst_node_ids: set[int] = set()
474
+ for message_id in message_ids:
475
+ in_message = found_message_ins_dict[message_id]
476
+ sint_node_id = convert_uint64_to_sint64(in_message.metadata.dst_node_id)
477
+ dst_node_ids.add(sint_node_id)
478
+ query = f"""
479
+ SELECT node_id, online_until
480
+ FROM node
481
+ WHERE node_id IN ({",".join(["?"] * len(dst_node_ids))});
482
+ """
483
+ rows = self.query(query, tuple(dst_node_ids))
484
+ tmp_ret_dict = check_node_availability_for_in_message(
485
+ inquired_in_message_ids=message_ids,
486
+ found_in_message_dict=found_message_ins_dict,
487
+ node_id_to_online_until={
488
+ convert_sint64_to_uint64(row["node_id"]): row["online_until"]
489
+ for row in rows
490
+ },
491
+ current_time=current,
492
+ )
493
+ ret.update(tmp_ret_dict)
494
+
468
495
  # Find all reply Messages
469
496
  query = f"""
470
497
  SELECT *
471
498
  FROM message_res
472
- WHERE reply_to_message IN ({",".join(["?"] * len(message_ids))})
499
+ WHERE reply_to_message_id IN ({",".join(["?"] * len(message_ids))})
473
500
  AND delivered_at = "";
474
501
  """
475
502
  rows = self.query(query, tuple(str(message_id) for message_id in message_ids))
@@ -542,7 +569,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
542
569
  # Delete reply Message
543
570
  query_2 = f"""
544
571
  DELETE FROM message_res
545
- WHERE reply_to_message IN ({placeholders});
572
+ WHERE reply_to_message_id IN ({placeholders});
546
573
  """
547
574
 
548
575
  with self.conn:
@@ -584,6 +611,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
584
611
  "VALUES (?, ?, ?, ?)"
585
612
  )
586
613
 
614
+ # Mark the node online util time.time() + ping_interval
587
615
  try:
588
616
  self.query(
589
617
  query,
@@ -699,7 +727,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
699
727
  fab_version: Optional[str],
700
728
  fab_hash: Optional[str],
701
729
  override_config: UserConfig,
702
- federation_options: ConfigsRecord,
730
+ federation_options: ConfigRecord,
703
731
  ) -> int:
704
732
  """Create a new run for the specified `fab_id` and `fab_version`."""
705
733
  # Sample a random int64 as run_id
@@ -725,7 +753,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
725
753
  fab_version,
726
754
  fab_hash,
727
755
  override_config_json,
728
- configsrecord_to_bytes(federation_options),
756
+ configrecord_to_bytes(federation_options),
729
757
  ]
730
758
  data += [
731
759
  now().isoformat(),
@@ -883,7 +911,7 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
883
911
 
884
912
  return pending_run_id
885
913
 
886
- def get_federation_options(self, run_id: int) -> Optional[ConfigsRecord]:
914
+ def get_federation_options(self, run_id: int) -> Optional[ConfigRecord]:
887
915
  """Retrieve the federation options for the specified `run_id`."""
888
916
  # Convert the uint64 value to sint64 for SQLite
889
917
  sint64_run_id = convert_uint64_to_sint64(run_id)
@@ -896,10 +924,14 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
896
924
  return None
897
925
 
898
926
  row = rows[0]
899
- return configsrecord_from_bytes(row["federation_options"])
927
+ return configrecord_from_bytes(row["federation_options"])
900
928
 
901
929
  def acknowledge_ping(self, node_id: int, ping_interval: float) -> bool:
902
- """Acknowledge a ping received from a node, serving as a heartbeat."""
930
+ """Acknowledge a ping received from a node, serving as a heartbeat.
931
+
932
+ It allows for one missed ping (in a PING_PATIENCE * ping_interval) before
933
+ marking the node as offline, where PING_PATIENCE = 2 in default.
934
+ """
903
935
  sint64_node_id = convert_uint64_to_sint64(node_id)
904
936
 
905
937
  # Check if the node exists in the `node` table
@@ -909,7 +941,14 @@ class SqliteLinkState(LinkState): # pylint: disable=R0904
909
941
 
910
942
  # Update `online_until` and `ping_interval` for the given `node_id`
911
943
  query = "UPDATE node SET online_until = ?, ping_interval = ? WHERE node_id = ?"
912
- self.query(query, (time.time() + ping_interval, ping_interval, sint64_node_id))
944
+ self.query(
945
+ query,
946
+ (
947
+ time.time() + PING_PATIENCE * ping_interval,
948
+ ping_interval,
949
+ sint64_node_id,
950
+ ),
951
+ )
913
952
  return True
914
953
 
915
954
  def get_serverapp_context(self, run_id: int) -> Optional[Context]:
@@ -1026,7 +1065,7 @@ def message_to_dict(message: Message) -> dict[str, Any]:
1026
1065
  "run_id": message.metadata.run_id,
1027
1066
  "src_node_id": message.metadata.src_node_id,
1028
1067
  "dst_node_id": message.metadata.dst_node_id,
1029
- "reply_to_message": message.metadata.reply_to_message,
1068
+ "reply_to_message_id": message.metadata.reply_to_message_id,
1030
1069
  "created_at": message.metadata.created_at,
1031
1070
  "delivered_at": message.metadata.delivered_at,
1032
1071
  "ttl": message.metadata.ttl,
@@ -1036,7 +1075,7 @@ def message_to_dict(message: Message) -> dict[str, Any]:
1036
1075
  }
1037
1076
 
1038
1077
  if message.has_content():
1039
- result["content"] = recordset_to_proto(message.content).SerializeToString()
1078
+ result["content"] = recorddict_to_proto(message.content).SerializeToString()
1040
1079
  else:
1041
1080
  result["error"] = error_to_proto(message.error).SerializeToString()
1042
1081
 
@@ -1047,20 +1086,15 @@ def dict_to_message(message_dict: dict[str, Any]) -> Message:
1047
1086
  """Transform dict to Message."""
1048
1087
  content, error = None, None
1049
1088
  if (b_content := message_dict.pop("content")) is not None:
1050
- content = recordset_from_proto(ProtoRecordSet.FromString(b_content))
1089
+ content = recorddict_from_proto(ProtoRecordDict.FromString(b_content))
1051
1090
  if (b_error := message_dict.pop("error")) is not None:
1052
1091
  error = error_from_proto(ProtoError.FromString(b_error))
1053
1092
 
1054
1093
  # Metadata constructor doesn't allow passing created_at. We set it later
1055
1094
  metadata = Metadata(
1056
- **{
1057
- k: v
1058
- for k, v in message_dict.items()
1059
- if k not in ["created_at", "delivered_at"]
1060
- }
1095
+ **{k: v for k, v in message_dict.items() if k not in ["delivered_at"]}
1061
1096
  )
1062
- msg = Message(metadata=metadata, content=content, error=error)
1063
- msg.metadata.__dict__["_created_at"] = message_dict["created_at"]
1097
+ msg = make_message(metadata=metadata, content=content, error=error)
1064
1098
  msg.metadata.delivered_at = message_dict["delivered_at"]
1065
1099
  return msg
1066
1100