flwr 1.23.0__py3-none-any.whl → 1.25.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 (339) hide show
  1. flwr/__init__.py +16 -5
  2. flwr/app/error.py +2 -2
  3. flwr/app/exception.py +3 -3
  4. flwr/cli/app.py +19 -0
  5. flwr/cli/{new/templates → app_cmd}/__init__.py +9 -1
  6. flwr/cli/app_cmd/publish.py +285 -0
  7. flwr/cli/app_cmd/review.py +262 -0
  8. flwr/cli/auth_plugin/auth_plugin.py +4 -5
  9. flwr/cli/auth_plugin/noop_auth_plugin.py +54 -11
  10. flwr/cli/auth_plugin/oidc_cli_plugin.py +32 -9
  11. flwr/cli/build.py +60 -18
  12. flwr/cli/cli_account_auth_interceptor.py +24 -7
  13. flwr/cli/config_utils.py +101 -13
  14. flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +10 -1
  15. flwr/cli/federation/ls.py +140 -0
  16. flwr/cli/federation/show.py +318 -0
  17. flwr/cli/install.py +91 -13
  18. flwr/cli/log.py +52 -9
  19. flwr/cli/login/login.py +7 -4
  20. flwr/cli/ls.py +211 -130
  21. flwr/cli/new/new.py +123 -331
  22. flwr/cli/pull.py +10 -5
  23. flwr/cli/run/run.py +71 -29
  24. flwr/cli/run_utils.py +148 -0
  25. flwr/cli/stop.py +26 -8
  26. flwr/cli/supernode/ls.py +25 -12
  27. flwr/cli/supernode/register.py +9 -4
  28. flwr/cli/supernode/unregister.py +5 -3
  29. flwr/cli/utils.py +239 -16
  30. flwr/client/__init__.py +1 -1
  31. flwr/client/dpfedavg_numpy_client.py +4 -1
  32. flwr/client/grpc_adapter_client/connection.py +8 -9
  33. flwr/client/grpc_rere_client/connection.py +16 -14
  34. flwr/client/grpc_rere_client/grpc_adapter.py +6 -2
  35. flwr/client/grpc_rere_client/node_auth_client_interceptor.py +2 -1
  36. flwr/client/message_handler/message_handler.py +2 -2
  37. flwr/client/mod/secure_aggregation/secaggplus_mod.py +3 -3
  38. flwr/client/numpy_client.py +1 -1
  39. flwr/client/rest_client/connection.py +18 -18
  40. flwr/client/run_info_store.py +4 -5
  41. flwr/client/typing.py +1 -1
  42. flwr/clientapp/client_app.py +9 -10
  43. flwr/clientapp/mod/centraldp_mods.py +16 -17
  44. flwr/clientapp/mod/localdp_mod.py +8 -9
  45. flwr/clientapp/typing.py +1 -1
  46. flwr/clientapp/utils.py +3 -3
  47. flwr/common/address.py +1 -2
  48. flwr/common/args.py +3 -4
  49. flwr/common/config.py +13 -16
  50. flwr/common/constant.py +5 -2
  51. flwr/common/differential_privacy.py +3 -4
  52. flwr/common/event_log_plugin/event_log_plugin.py +3 -4
  53. flwr/common/exit/exit.py +15 -2
  54. flwr/common/exit/exit_code.py +19 -0
  55. flwr/common/exit/exit_handler.py +6 -2
  56. flwr/common/exit/signal_handler.py +5 -5
  57. flwr/common/grpc.py +6 -6
  58. flwr/common/inflatable_protobuf_utils.py +1 -1
  59. flwr/common/inflatable_utils.py +38 -21
  60. flwr/common/logger.py +19 -19
  61. flwr/common/message.py +4 -4
  62. flwr/common/object_ref.py +7 -7
  63. flwr/common/record/array.py +3 -3
  64. flwr/common/record/arrayrecord.py +18 -30
  65. flwr/common/record/configrecord.py +3 -3
  66. flwr/common/record/recorddict.py +5 -5
  67. flwr/common/record/typeddict.py +9 -2
  68. flwr/common/recorddict_compat.py +7 -10
  69. flwr/common/retry_invoker.py +20 -20
  70. flwr/common/secure_aggregation/ndarrays_arithmetic.py +3 -3
  71. flwr/common/serde.py +11 -4
  72. flwr/common/serde_utils.py +2 -2
  73. flwr/common/telemetry.py +9 -5
  74. flwr/common/typing.py +58 -37
  75. flwr/compat/client/app.py +38 -37
  76. flwr/compat/client/grpc_client/connection.py +11 -11
  77. flwr/compat/server/app.py +5 -6
  78. flwr/proto/appio_pb2.py +13 -3
  79. flwr/proto/appio_pb2.pyi +134 -65
  80. flwr/proto/appio_pb2_grpc.py +20 -0
  81. flwr/proto/appio_pb2_grpc.pyi +27 -0
  82. flwr/proto/clientappio_pb2.py +17 -7
  83. flwr/proto/clientappio_pb2.pyi +15 -0
  84. flwr/proto/clientappio_pb2_grpc.py +206 -40
  85. flwr/proto/clientappio_pb2_grpc.pyi +168 -53
  86. flwr/proto/control_pb2.py +71 -52
  87. flwr/proto/control_pb2.pyi +277 -111
  88. flwr/proto/control_pb2_grpc.py +249 -40
  89. flwr/proto/control_pb2_grpc.pyi +185 -52
  90. flwr/proto/error_pb2.py +13 -3
  91. flwr/proto/error_pb2.pyi +24 -6
  92. flwr/proto/error_pb2_grpc.py +20 -0
  93. flwr/proto/error_pb2_grpc.pyi +27 -0
  94. flwr/proto/fab_pb2.py +14 -4
  95. flwr/proto/fab_pb2.pyi +59 -31
  96. flwr/proto/fab_pb2_grpc.py +20 -0
  97. flwr/proto/fab_pb2_grpc.pyi +27 -0
  98. flwr/proto/federation_pb2.py +38 -0
  99. flwr/proto/federation_pb2.pyi +56 -0
  100. flwr/proto/federation_pb2_grpc.py +24 -0
  101. flwr/proto/federation_pb2_grpc.pyi +31 -0
  102. flwr/proto/fleet_pb2.py +24 -14
  103. flwr/proto/fleet_pb2.pyi +141 -61
  104. flwr/proto/fleet_pb2_grpc.py +189 -48
  105. flwr/proto/fleet_pb2_grpc.pyi +175 -61
  106. flwr/proto/grpcadapter_pb2.py +14 -4
  107. flwr/proto/grpcadapter_pb2.pyi +38 -16
  108. flwr/proto/grpcadapter_pb2_grpc.py +35 -4
  109. flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
  110. flwr/proto/heartbeat_pb2.py +17 -7
  111. flwr/proto/heartbeat_pb2.pyi +51 -22
  112. flwr/proto/heartbeat_pb2_grpc.py +20 -0
  113. flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
  114. flwr/proto/log_pb2.py +13 -3
  115. flwr/proto/log_pb2.pyi +34 -11
  116. flwr/proto/log_pb2_grpc.py +20 -0
  117. flwr/proto/log_pb2_grpc.pyi +27 -0
  118. flwr/proto/message_pb2.py +15 -5
  119. flwr/proto/message_pb2.pyi +154 -86
  120. flwr/proto/message_pb2_grpc.py +20 -0
  121. flwr/proto/message_pb2_grpc.pyi +27 -0
  122. flwr/proto/node_pb2.py +15 -5
  123. flwr/proto/node_pb2.pyi +50 -25
  124. flwr/proto/node_pb2_grpc.py +20 -0
  125. flwr/proto/node_pb2_grpc.pyi +27 -0
  126. flwr/proto/recorddict_pb2.py +13 -3
  127. flwr/proto/recorddict_pb2.pyi +184 -107
  128. flwr/proto/recorddict_pb2_grpc.py +20 -0
  129. flwr/proto/recorddict_pb2_grpc.pyi +27 -0
  130. flwr/proto/run_pb2.py +40 -31
  131. flwr/proto/run_pb2.pyi +158 -84
  132. flwr/proto/run_pb2_grpc.py +20 -0
  133. flwr/proto/run_pb2_grpc.pyi +27 -0
  134. flwr/proto/serverappio_pb2.py +13 -3
  135. flwr/proto/serverappio_pb2.pyi +32 -8
  136. flwr/proto/serverappio_pb2_grpc.py +246 -65
  137. flwr/proto/serverappio_pb2_grpc.pyi +221 -85
  138. flwr/proto/simulationio_pb2.py +16 -8
  139. flwr/proto/simulationio_pb2.pyi +15 -0
  140. flwr/proto/simulationio_pb2_grpc.py +162 -41
  141. flwr/proto/simulationio_pb2_grpc.pyi +149 -55
  142. flwr/proto/transport_pb2.py +20 -10
  143. flwr/proto/transport_pb2.pyi +249 -160
  144. flwr/proto/transport_pb2_grpc.py +35 -4
  145. flwr/proto/transport_pb2_grpc.pyi +38 -8
  146. flwr/server/app.py +39 -17
  147. flwr/server/client_manager.py +4 -5
  148. flwr/server/client_proxy.py +10 -11
  149. flwr/server/compat/app.py +4 -5
  150. flwr/server/compat/app_utils.py +2 -1
  151. flwr/server/compat/grid_client_proxy.py +10 -12
  152. flwr/server/compat/legacy_context.py +3 -4
  153. flwr/server/fleet_event_log_interceptor.py +2 -1
  154. flwr/server/grid/grid.py +2 -3
  155. flwr/server/grid/grpc_grid.py +10 -8
  156. flwr/server/grid/inmemory_grid.py +4 -4
  157. flwr/server/run_serverapp.py +2 -3
  158. flwr/server/server.py +34 -39
  159. flwr/server/server_app.py +7 -8
  160. flwr/server/server_config.py +1 -2
  161. flwr/server/serverapp/app.py +34 -28
  162. flwr/server/serverapp_components.py +4 -5
  163. flwr/server/strategy/aggregate.py +9 -8
  164. flwr/server/strategy/bulyan.py +13 -11
  165. flwr/server/strategy/dp_adaptive_clipping.py +16 -20
  166. flwr/server/strategy/dp_fixed_clipping.py +12 -17
  167. flwr/server/strategy/dpfedavg_adaptive.py +3 -4
  168. flwr/server/strategy/dpfedavg_fixed.py +6 -10
  169. flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
  170. flwr/server/strategy/fedadagrad.py +18 -14
  171. flwr/server/strategy/fedadam.py +16 -14
  172. flwr/server/strategy/fedavg.py +16 -17
  173. flwr/server/strategy/fedavg_android.py +15 -15
  174. flwr/server/strategy/fedavgm.py +21 -18
  175. flwr/server/strategy/fedmedian.py +2 -3
  176. flwr/server/strategy/fedopt.py +11 -10
  177. flwr/server/strategy/fedprox.py +10 -9
  178. flwr/server/strategy/fedtrimmedavg.py +12 -11
  179. flwr/server/strategy/fedxgb_bagging.py +13 -11
  180. flwr/server/strategy/fedxgb_cyclic.py +6 -6
  181. flwr/server/strategy/fedxgb_nn_avg.py +4 -4
  182. flwr/server/strategy/fedyogi.py +16 -14
  183. flwr/server/strategy/krum.py +12 -11
  184. flwr/server/strategy/qfedavg.py +16 -15
  185. flwr/server/strategy/strategy.py +6 -9
  186. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +2 -1
  187. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
  188. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
  189. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
  190. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
  191. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +4 -4
  192. flwr/server/superlink/fleet/grpc_rere/node_auth_server_interceptor.py +3 -2
  193. flwr/server/superlink/fleet/message_handler/message_handler.py +75 -30
  194. flwr/server/superlink/fleet/rest_rere/rest_api.py +2 -2
  195. flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
  196. flwr/server/superlink/fleet/vce/backend/raybackend.py +5 -5
  197. flwr/server/superlink/fleet/vce/vce_api.py +15 -9
  198. flwr/server/superlink/linkstate/in_memory_linkstate.py +148 -149
  199. flwr/server/superlink/linkstate/linkstate.py +91 -43
  200. flwr/server/superlink/linkstate/linkstate_factory.py +22 -5
  201. flwr/server/superlink/linkstate/sqlite_linkstate.py +502 -436
  202. flwr/server/superlink/linkstate/utils.py +6 -6
  203. flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
  204. flwr/server/superlink/serverappio/serverappio_servicer.py +26 -21
  205. flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
  206. flwr/server/superlink/simulation/simulationio_servicer.py +18 -13
  207. flwr/server/superlink/utils.py +4 -6
  208. flwr/server/typing.py +1 -1
  209. flwr/server/utils/tensorboard.py +15 -8
  210. flwr/server/workflow/default_workflows.py +5 -5
  211. flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
  212. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +8 -8
  213. flwr/serverapp/strategy/bulyan.py +16 -15
  214. flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
  215. flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
  216. flwr/serverapp/strategy/fedadagrad.py +10 -11
  217. flwr/serverapp/strategy/fedadam.py +10 -11
  218. flwr/serverapp/strategy/fedavg.py +9 -10
  219. flwr/serverapp/strategy/fedavgm.py +17 -16
  220. flwr/serverapp/strategy/fedmedian.py +2 -2
  221. flwr/serverapp/strategy/fedopt.py +10 -11
  222. flwr/serverapp/strategy/fedprox.py +7 -8
  223. flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
  224. flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
  225. flwr/serverapp/strategy/fedxgb_cyclic.py +9 -9
  226. flwr/serverapp/strategy/fedyogi.py +9 -11
  227. flwr/serverapp/strategy/krum.py +7 -7
  228. flwr/serverapp/strategy/multikrum.py +9 -9
  229. flwr/serverapp/strategy/qfedavg.py +17 -16
  230. flwr/serverapp/strategy/strategy.py +6 -9
  231. flwr/serverapp/strategy/strategy_utils.py +7 -8
  232. flwr/simulation/app.py +46 -42
  233. flwr/simulation/legacy_app.py +12 -12
  234. flwr/simulation/ray_transport/ray_actor.py +10 -11
  235. flwr/simulation/ray_transport/ray_client_proxy.py +11 -12
  236. flwr/simulation/run_simulation.py +43 -43
  237. flwr/simulation/simulationio_connection.py +4 -4
  238. flwr/supercore/cli/flower_superexec.py +3 -4
  239. flwr/supercore/constant.py +34 -1
  240. flwr/supercore/corestate/corestate.py +24 -3
  241. flwr/supercore/corestate/in_memory_corestate.py +138 -0
  242. flwr/supercore/corestate/sqlite_corestate.py +157 -0
  243. flwr/supercore/ffs/disk_ffs.py +1 -2
  244. flwr/supercore/ffs/ffs.py +1 -2
  245. flwr/supercore/ffs/ffs_factory.py +1 -2
  246. flwr/{common → supercore}/heartbeat.py +20 -25
  247. flwr/supercore/object_store/in_memory_object_store.py +1 -2
  248. flwr/supercore/object_store/object_store.py +1 -2
  249. flwr/supercore/object_store/object_store_factory.py +1 -2
  250. flwr/supercore/object_store/sqlite_object_store.py +8 -7
  251. flwr/supercore/primitives/asymmetric.py +1 -1
  252. flwr/supercore/primitives/asymmetric_ed25519.py +11 -1
  253. flwr/supercore/sqlite_mixin.py +37 -34
  254. flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
  255. flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
  256. flwr/supercore/superexec/run_superexec.py +9 -13
  257. flwr/supercore/utils.py +190 -0
  258. flwr/superlink/artifact_provider/artifact_provider.py +1 -2
  259. flwr/superlink/auth_plugin/auth_plugin.py +6 -9
  260. flwr/superlink/auth_plugin/noop_auth_plugin.py +6 -9
  261. flwr/{cli/new/templates/app → superlink/federation}/__init__.py +10 -1
  262. flwr/superlink/federation/federation_manager.py +64 -0
  263. flwr/superlink/federation/noop_federation_manager.py +71 -0
  264. flwr/superlink/servicer/control/control_account_auth_interceptor.py +22 -13
  265. flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
  266. flwr/superlink/servicer/control/control_grpc.py +7 -6
  267. flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
  268. flwr/superlink/servicer/control/control_servicer.py +190 -23
  269. flwr/supernode/cli/flower_supernode.py +58 -3
  270. flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
  271. flwr/supernode/nodestate/nodestate.py +52 -8
  272. flwr/supernode/nodestate/nodestate_factory.py +7 -4
  273. flwr/supernode/runtime/run_clientapp.py +41 -22
  274. flwr/supernode/servicer/clientappio/clientappio_servicer.py +46 -10
  275. flwr/supernode/start_client_internal.py +165 -46
  276. {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/METADATA +9 -11
  277. flwr-1.25.0.dist-info/RECORD +393 -0
  278. flwr/cli/new/templates/app/.gitignore.tpl +0 -163
  279. flwr/cli/new/templates/app/LICENSE.tpl +0 -202
  280. flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
  281. flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
  282. flwr/cli/new/templates/app/README.md.tpl +0 -37
  283. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
  284. flwr/cli/new/templates/app/code/__init__.py +0 -15
  285. flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
  286. flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
  287. flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
  288. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
  289. flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
  290. flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
  291. flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
  292. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
  293. flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
  294. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
  295. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
  296. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
  297. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
  298. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
  299. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
  300. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
  301. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
  302. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
  303. flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
  304. flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
  305. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
  306. flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
  307. flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
  308. flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
  309. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
  310. flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
  311. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
  312. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
  313. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
  314. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
  315. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
  316. flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
  317. flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
  318. flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
  319. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
  320. flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
  321. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
  322. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
  323. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
  324. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
  325. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
  326. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
  327. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
  328. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
  329. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
  330. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
  331. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
  332. flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
  333. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
  334. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
  335. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
  336. flwr/supercore/object_store/utils.py +0 -43
  337. flwr-1.23.0.dist-info/RECORD +0 -439
  338. {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/WHEEL +0 -0
  339. {flwr-1.23.0.dist-info → flwr-1.25.0.dist-info}/entry_points.txt +0 -0
@@ -16,14 +16,13 @@
16
16
 
17
17
 
18
18
  from abc import ABC, abstractmethod
19
- from typing import Optional
20
19
 
21
20
 
22
21
  class ArtifactProvider(ABC):
23
22
  """ArtifactProvider interface for providing artifact download links."""
24
23
 
25
24
  @abstractmethod
26
- def get_url(self, run_id: int) -> Optional[str]:
25
+ def get_url(self, run_id: int) -> str | None:
27
26
  """Return the artifact download link for the given run ID."""
28
27
 
29
28
  @property
@@ -18,7 +18,6 @@
18
18
  from abc import ABC, abstractmethod
19
19
  from collections.abc import Sequence
20
20
  from pathlib import Path
21
- from typing import Optional, Union
22
21
 
23
22
  from flwr.common.typing import (
24
23
  AccountAuthCredentials,
@@ -48,25 +47,23 @@ class ControlAuthnPlugin(ABC):
48
47
  """Abstract constructor."""
49
48
 
50
49
  @abstractmethod
51
- def get_login_details(self) -> Optional[AccountAuthLoginDetails]:
50
+ def get_login_details(self) -> AccountAuthLoginDetails | None:
52
51
  """Get the login details."""
53
52
 
54
53
  @abstractmethod
55
54
  def validate_tokens_in_metadata(
56
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
57
- ) -> tuple[bool, Optional[AccountInfo]]:
55
+ self, metadata: Sequence[tuple[str, str | bytes]]
56
+ ) -> tuple[bool, AccountInfo | None]:
58
57
  """Validate authentication tokens in the provided metadata."""
59
58
 
60
59
  @abstractmethod
61
- def get_auth_tokens(self, device_code: str) -> Optional[AccountAuthCredentials]:
60
+ def get_auth_tokens(self, device_code: str) -> AccountAuthCredentials | None:
62
61
  """Get authentication tokens."""
63
62
 
64
63
  @abstractmethod
65
64
  def refresh_tokens(
66
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
67
- ) -> tuple[
68
- Optional[Sequence[tuple[str, Union[str, bytes]]]], Optional[AccountInfo]
69
- ]:
65
+ self, metadata: Sequence[tuple[str, str | bytes]]
66
+ ) -> tuple[Sequence[tuple[str, str | bytes]] | None, AccountInfo | None]:
70
67
  """Refresh authentication tokens in the provided metadata."""
71
68
 
72
69
 
@@ -18,7 +18,6 @@ authorization plugins."""
18
18
 
19
19
  from collections.abc import Sequence
20
20
  from pathlib import Path
21
- from typing import Optional, Union
22
21
 
23
22
  from flwr.common.constant import NOOP_ACCOUNT_NAME, NOOP_FLWR_AID, AuthnType
24
23
  from flwr.common.typing import (
@@ -45,7 +44,7 @@ class NoOpControlAuthnPlugin(ControlAuthnPlugin):
45
44
  ):
46
45
  pass
47
46
 
48
- def get_login_details(self) -> Optional[AccountAuthLoginDetails]:
47
+ def get_login_details(self) -> AccountAuthLoginDetails | None:
49
48
  """Get the login details."""
50
49
  # This allows the `flwr login` command to load the NoOp plugin accordingly,
51
50
  # which then raises a LoginError when attempting to login.
@@ -58,20 +57,18 @@ class NoOpControlAuthnPlugin(ControlAuthnPlugin):
58
57
  )
59
58
 
60
59
  def validate_tokens_in_metadata(
61
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
62
- ) -> tuple[bool, Optional[AccountInfo]]:
60
+ self, metadata: Sequence[tuple[str, str | bytes]]
61
+ ) -> tuple[bool, AccountInfo | None]:
63
62
  """Return valid for no-op plugin."""
64
63
  return True, NOOP_ACCOUNT_INFO
65
64
 
66
- def get_auth_tokens(self, device_code: str) -> Optional[AccountAuthCredentials]:
65
+ def get_auth_tokens(self, device_code: str) -> AccountAuthCredentials | None:
67
66
  """Get authentication tokens."""
68
67
  raise RuntimeError("NoOp plugin does not support getting auth tokens.")
69
68
 
70
69
  def refresh_tokens(
71
- self, metadata: Sequence[tuple[str, Union[str, bytes]]]
72
- ) -> tuple[
73
- Optional[Sequence[tuple[str, Union[str, bytes]]]], Optional[AccountInfo]
74
- ]:
70
+ self, metadata: Sequence[tuple[str, str | bytes]]
71
+ ) -> tuple[Sequence[tuple[str, str | bytes]] | None, AccountInfo | None]:
75
72
  """Refresh authentication tokens in the provided metadata."""
76
73
  return metadata, NOOP_ACCOUNT_INFO
77
74
 
@@ -12,4 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  # ==============================================================================
15
- """Flower CLI `new` command app templates."""
15
+ """Federation Managers."""
16
+
17
+
18
+ from .federation_manager import FederationManager
19
+ from .noop_federation_manager import NoOpFederationManager
20
+
21
+ __all__ = [
22
+ "FederationManager",
23
+ "NoOpFederationManager",
24
+ ]
@@ -0,0 +1,64 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Abstract base class FederationManager."""
16
+
17
+
18
+ from abc import ABC, abstractmethod
19
+ from typing import TYPE_CHECKING
20
+
21
+ from flwr.common.typing import Federation
22
+
23
+ if TYPE_CHECKING:
24
+ from flwr.server.superlink.linkstate.linkstate import LinkState
25
+
26
+
27
+ class FederationManager(ABC):
28
+ """Abstract base class for FederationManager."""
29
+
30
+ @property
31
+ def linkstate(self) -> "LinkState":
32
+ """Return the LinkState instance."""
33
+ if not (ret := getattr(self, "_linkstate", None)):
34
+ raise RuntimeError("linkstate not set. Assign to linkstate property first.")
35
+ return ret # type: ignore
36
+
37
+ @linkstate.setter
38
+ def linkstate(self, linkstate: "LinkState") -> None:
39
+ """Set the LinkState instance."""
40
+ self._linkstate = linkstate
41
+
42
+ @abstractmethod
43
+ def exists(self, federation: str) -> bool:
44
+ """Check if a federation exists."""
45
+
46
+ @abstractmethod
47
+ def has_member(self, flwr_aid: str, federation: str) -> bool:
48
+ """Check if the given account is a member of the federation."""
49
+
50
+ @abstractmethod
51
+ def filter_nodes(self, node_ids: set[int], federation: str) -> set[int]:
52
+ """Given a list of node IDs, return sublist with nodes in federation."""
53
+
54
+ @abstractmethod
55
+ def has_node(self, node_id: int, federation: str) -> bool:
56
+ """Given a node ID, check if it is in the federation."""
57
+
58
+ @abstractmethod
59
+ def get_federations(self, flwr_aid: str) -> list[str]:
60
+ """Get federations of which the account is a member."""
61
+
62
+ @abstractmethod
63
+ def get_details(self, federation: str) -> Federation:
64
+ """Get details of the federation."""
@@ -0,0 +1,71 @@
1
+ # Copyright 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """NoOp implementation of FederationManager."""
16
+
17
+
18
+ from flwr.common.constant import NOOP_FLWR_AID
19
+ from flwr.common.typing import Federation
20
+ from flwr.supercore.constant import NOOP_FEDERATION
21
+
22
+ from .federation_manager import FederationManager
23
+
24
+
25
+ class NoOpFederationManager(FederationManager):
26
+ """No-Op FederationManager implementation."""
27
+
28
+ def exists(self, federation: str) -> bool:
29
+ """Check if a federation exists."""
30
+ return federation == NOOP_FEDERATION
31
+
32
+ def has_member(self, flwr_aid: str, federation: str) -> bool:
33
+ """Check if the given account is a member of the federation."""
34
+ if not self.exists(federation):
35
+ raise ValueError(f"Federation '{federation}' does not exist.")
36
+ return flwr_aid == NOOP_FLWR_AID
37
+
38
+ def filter_nodes(self, node_ids: set[int], federation: str) -> set[int]:
39
+ """Given a list of node IDs, return sublist with nodes in federation."""
40
+ if not self.exists(federation):
41
+ raise ValueError(f"Federation '{federation}' does not exist.")
42
+ return node_ids
43
+
44
+ def has_node(self, node_id: int, federation: str) -> bool:
45
+ """Given a node ID, check if it is in the federation."""
46
+ if not self.exists(federation):
47
+ raise ValueError(f"Federation '{federation}' does not exist.")
48
+ return True
49
+
50
+ def get_federations(self, flwr_aid: str) -> list[str]:
51
+ """Get federations of which the account is a member."""
52
+ if flwr_aid != NOOP_FLWR_AID:
53
+ return []
54
+ return [NOOP_FEDERATION]
55
+
56
+ def get_details(self, federation: str) -> Federation:
57
+ """Get details of the federation."""
58
+ if federation != NOOP_FEDERATION:
59
+ raise ValueError(f"Federation '{federation}' does not exist.")
60
+
61
+ run_ids = self.linkstate.get_run_ids(flwr_aid=NOOP_FLWR_AID)
62
+ nodes = list(self.linkstate.get_node_info(owner_aids=[NOOP_FLWR_AID]))
63
+ runs = [
64
+ run for run_id in run_ids if (run := self.linkstate.get_run(run_id=run_id))
65
+ ]
66
+ return Federation(
67
+ name=NOOP_FEDERATION,
68
+ member_aids=[NOOP_FLWR_AID],
69
+ nodes=nodes,
70
+ runs=runs,
71
+ )
@@ -16,7 +16,8 @@
16
16
 
17
17
 
18
18
  import contextvars
19
- from typing import Any, Callable, Union
19
+ from collections.abc import Callable
20
+ from typing import Any
20
21
 
21
22
  import grpc
22
23
 
@@ -33,23 +34,31 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
33
34
  )
34
35
  from flwr.superlink.auth_plugin import ControlAuthnPlugin, ControlAuthzPlugin
35
36
 
36
- Request = Union[
37
- StartRunRequest,
38
- StreamLogsRequest,
39
- GetLoginDetailsRequest,
40
- GetAuthTokensRequest,
41
- ]
37
+ Request = (
38
+ StartRunRequest | StreamLogsRequest | GetLoginDetailsRequest | GetAuthTokensRequest
39
+ )
42
40
 
43
- Response = Union[
44
- StartRunResponse, StreamLogsResponse, GetLoginDetailsResponse, GetAuthTokensResponse
45
- ]
41
+ Response = (
42
+ StartRunResponse
43
+ | StreamLogsResponse
44
+ | GetLoginDetailsResponse
45
+ | GetAuthTokensResponse
46
+ )
46
47
 
47
48
 
48
- shared_account_info: contextvars.ContextVar[AccountInfo] = contextvars.ContextVar(
49
- "account_info", default=AccountInfo(flwr_aid=None, account_name=None)
49
+ shared_account_info: contextvars.ContextVar[AccountInfo | None] = (
50
+ contextvars.ContextVar("account_info", default=None)
50
51
  )
51
52
 
52
53
 
54
+ def get_current_account_info() -> AccountInfo:
55
+ """Get the current account info from context, or return a default if not set."""
56
+ account_info = shared_account_info.get()
57
+ if account_info is None:
58
+ return AccountInfo(flwr_aid=None, account_name=None)
59
+ return account_info
60
+
61
+
53
62
  class ControlAccountAuthInterceptor(grpc.ServerInterceptor): # type: ignore
54
63
  """Control API interceptor for account authentication."""
55
64
 
@@ -93,7 +102,7 @@ class ControlAccountAuthInterceptor(grpc.ServerInterceptor): # type: ignore
93
102
 
94
103
  # Intercept GetLoginDetails and GetAuthTokens requests, and return
95
104
  # the response without authentication
96
- if isinstance(request, (GetLoginDetailsRequest, GetAuthTokensRequest)):
105
+ if isinstance(request, (GetLoginDetailsRequest | GetAuthTokensRequest)):
97
106
  return call(request, context) # type: ignore
98
107
 
99
108
  # For other requests, check if the account is authenticated
@@ -15,8 +15,8 @@
15
15
  """Flower Control API event log interceptor."""
16
16
 
17
17
 
18
- from collections.abc import Iterator
19
- from typing import Any, Callable, Union, cast
18
+ from collections.abc import Callable, Iterator
19
+ from typing import Any, cast
20
20
 
21
21
  import grpc
22
22
  from google.protobuf.message import Message as GrpcMessage
@@ -24,7 +24,7 @@ from google.protobuf.message import Message as GrpcMessage
24
24
  from flwr.common.event_log_plugin.event_log_plugin import EventLogWriterPlugin
25
25
  from flwr.common.typing import LogEntry
26
26
 
27
- from .control_account_auth_interceptor import shared_account_info
27
+ from .control_account_auth_interceptor import get_current_account_info
28
28
 
29
29
 
30
30
  class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
@@ -60,13 +60,13 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
60
60
  def _generic_method_handler(
61
61
  request: GrpcMessage,
62
62
  context: grpc.ServicerContext,
63
- ) -> Union[GrpcMessage, Iterator[GrpcMessage], BaseException]:
63
+ ) -> GrpcMessage | Iterator[GrpcMessage] | BaseException:
64
64
  log_entry: LogEntry
65
65
  # Log before call
66
66
  log_entry = self.log_plugin.compose_log_before_event(
67
67
  request=request,
68
68
  context=context,
69
- account_info=shared_account_info.get(),
69
+ account_info=get_current_account_info(),
70
70
  method_name=method_name,
71
71
  )
72
72
  self.log_plugin.write_log(log_entry)
@@ -85,7 +85,7 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
85
85
  log_entry = self.log_plugin.compose_log_after_event(
86
86
  request=request,
87
87
  context=context,
88
- account_info=shared_account_info.get(),
88
+ account_info=get_current_account_info(),
89
89
  method_name=method_name,
90
90
  response=unary_response or error,
91
91
  )
@@ -115,7 +115,7 @@ class ControlEventLogInterceptor(grpc.ServerInterceptor): # type: ignore
115
115
  log_entry = self.log_plugin.compose_log_after_event(
116
116
  request=request,
117
117
  context=context,
118
- account_info=shared_account_info.get(),
118
+ account_info=get_current_account_info(),
119
119
  method_name=method_name,
120
120
  response=stream_response or error,
121
121
  )
@@ -16,7 +16,6 @@
16
16
 
17
17
 
18
18
  from logging import INFO
19
- from typing import Optional
20
19
 
21
20
  import grpc
22
21
 
@@ -46,7 +45,7 @@ try:
46
45
  from flwr.ee import get_license_plugin
47
46
  except ImportError:
48
47
 
49
- def get_license_plugin() -> Optional[LicensePlugin]:
48
+ def get_license_plugin() -> LicensePlugin | None:
50
49
  """Return the license plugin."""
51
50
 
52
51
 
@@ -56,15 +55,16 @@ def run_control_api_grpc(
56
55
  state_factory: LinkStateFactory,
57
56
  ffs_factory: FfsFactory,
58
57
  objectstore_factory: ObjectStoreFactory,
59
- certificates: Optional[tuple[bytes, bytes, bytes]],
58
+ certificates: tuple[bytes, bytes, bytes] | None,
60
59
  is_simulation: bool,
61
60
  authn_plugin: ControlAuthnPlugin,
62
61
  authz_plugin: ControlAuthzPlugin,
63
- event_log_plugin: Optional[EventLogWriterPlugin] = None,
64
- artifact_provider: Optional[ArtifactProvider] = None,
62
+ event_log_plugin: EventLogWriterPlugin | None = None,
63
+ artifact_provider: ArtifactProvider | None = None,
64
+ fleet_api_type: str | None = None,
65
65
  ) -> grpc.Server:
66
66
  """Run Control API (gRPC, request-response)."""
67
- license_plugin: Optional[LicensePlugin] = get_license_plugin()
67
+ license_plugin: LicensePlugin | None = get_license_plugin()
68
68
  if license_plugin and not license_plugin.check_license():
69
69
  flwr_exit(ExitCode.SUPERLINK_LICENSE_INVALID)
70
70
 
@@ -75,6 +75,7 @@ def run_control_api_grpc(
75
75
  is_simulation=is_simulation,
76
76
  authn_plugin=authn_plugin,
77
77
  artifact_provider=artifact_provider,
78
+ fleet_api_type=fleet_api_type,
78
79
  )
79
80
  interceptors = [ControlAccountAuthInterceptor(authn_plugin, authz_plugin)]
80
81
  if license_plugin is not None:
@@ -15,8 +15,8 @@
15
15
  """Flower Control API license interceptor."""
16
16
 
17
17
 
18
- from collections.abc import Iterator
19
- from typing import Any, Callable, Union
18
+ from collections.abc import Callable, Iterator
19
+ from typing import Any
20
20
 
21
21
  import grpc
22
22
  from google.protobuf.message import Message as GrpcMessage
@@ -57,7 +57,7 @@ class ControlLicenseInterceptor(grpc.ServerInterceptor): # type: ignore
57
57
  def _generic_method_handler(
58
58
  request: GrpcMessage,
59
59
  context: grpc.ServicerContext,
60
- ) -> Union[GrpcMessage, Iterator[GrpcMessage]]:
60
+ ) -> GrpcMessage | Iterator[GrpcMessage]:
61
61
  """Handle the method call with license checking."""
62
62
  call = method_handler.unary_unary or method_handler.unary_stream
63
63