flwr-nightly 1.23.0.dev20250930__py3-none-any.whl → 1.26.0.dev20260121__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 (375) hide show
  1. flwr/__init__.py +17 -6
  2. flwr/app/__init__.py +4 -1
  3. flwr/app/error.py +2 -2
  4. flwr/app/exception.py +3 -3
  5. flwr/app/message_type.py +29 -0
  6. flwr/app/metadata.py +5 -2
  7. flwr/app/user_config.py +19 -0
  8. flwr/cli/app.py +62 -9
  9. flwr/cli/{new/templates/app/code → app_cmd}/__init__.py +9 -1
  10. flwr/cli/app_cmd/publish.py +285 -0
  11. flwr/cli/app_cmd/review.py +262 -0
  12. flwr/cli/auth_plugin/__init__.py +13 -6
  13. flwr/cli/auth_plugin/auth_plugin.py +26 -15
  14. flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
  15. flwr/cli/auth_plugin/oidc_cli_plugin.py +52 -32
  16. flwr/cli/build.py +166 -53
  17. flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +27 -10
  18. flwr/cli/config/__init__.py +21 -0
  19. flwr/cli/config/ls.py +104 -0
  20. flwr/cli/config_migration.py +300 -0
  21. flwr/cli/config_utils.py +154 -13
  22. flwr/cli/constant.py +67 -0
  23. flwr/cli/{new/templates/app/code/flwr_tune → federation}/__init__.py +8 -1
  24. flwr/cli/federation/ls.py +361 -0
  25. flwr/cli/flower_config.py +447 -0
  26. flwr/cli/install.py +91 -13
  27. flwr/cli/log.py +65 -36
  28. flwr/cli/login/login.py +41 -27
  29. flwr/cli/ls.py +232 -158
  30. flwr/cli/new/new.py +188 -244
  31. flwr/cli/pull.py +25 -34
  32. flwr/cli/run/run.py +106 -74
  33. flwr/cli/run_utils.py +148 -0
  34. flwr/cli/stop.py +46 -37
  35. flwr/cli/supernode/__init__.py +25 -0
  36. flwr/cli/supernode/ls.py +273 -0
  37. flwr/cli/supernode/register.py +190 -0
  38. flwr/cli/supernode/unregister.py +140 -0
  39. flwr/cli/typing.py +211 -0
  40. flwr/cli/utils.py +428 -80
  41. flwr/client/__init__.py +2 -1
  42. flwr/client/dpfedavg_numpy_client.py +4 -1
  43. flwr/client/grpc_adapter_client/connection.py +14 -17
  44. flwr/client/grpc_rere_client/connection.py +73 -43
  45. flwr/client/grpc_rere_client/grpc_adapter.py +35 -15
  46. flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
  47. flwr/client/message_handler/message_handler.py +4 -3
  48. flwr/client/mod/centraldp_mods.py +1 -1
  49. flwr/client/mod/localdp_mod.py +1 -1
  50. flwr/client/mod/secure_aggregation/secaggplus_mod.py +11 -9
  51. flwr/client/numpy_client.py +1 -1
  52. flwr/client/rest_client/connection.py +99 -54
  53. flwr/client/run_info_store.py +6 -6
  54. flwr/client/typing.py +1 -1
  55. flwr/clientapp/__init__.py +1 -2
  56. flwr/{client → clientapp}/client_app.py +11 -11
  57. flwr/clientapp/mod/centraldp_mods.py +16 -17
  58. flwr/clientapp/mod/localdp_mod.py +8 -9
  59. flwr/clientapp/typing.py +1 -1
  60. flwr/{client/clientapp → clientapp}/utils.py +4 -4
  61. flwr/common/__init__.py +3 -2
  62. flwr/common/args.py +3 -4
  63. flwr/common/config.py +15 -17
  64. flwr/common/constant.py +56 -28
  65. flwr/common/context.py +2 -1
  66. flwr/common/differential_privacy.py +3 -4
  67. flwr/common/event_log_plugin/event_log_plugin.py +3 -4
  68. flwr/common/exit/exit.py +16 -3
  69. flwr/common/exit/exit_code.py +39 -10
  70. flwr/common/exit/exit_handler.py +6 -2
  71. flwr/common/exit/signal_handler.py +5 -5
  72. flwr/common/grpc.py +8 -7
  73. flwr/common/inflatable_protobuf_utils.py +1 -1
  74. flwr/common/inflatable_utils.py +48 -31
  75. flwr/common/logger.py +19 -19
  76. flwr/common/message.py +5 -5
  77. flwr/common/object_ref.py +7 -7
  78. flwr/common/record/array.py +6 -6
  79. flwr/common/record/arrayrecord.py +18 -21
  80. flwr/common/record/configrecord.py +3 -3
  81. flwr/common/record/recorddict.py +5 -5
  82. flwr/common/record/typeddict.py +9 -2
  83. flwr/common/recorddict_compat.py +7 -10
  84. flwr/common/retry_invoker.py +20 -20
  85. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
  86. flwr/common/secure_aggregation/ndarrays_arithmetic.py +8 -5
  87. flwr/common/serde.py +22 -11
  88. flwr/common/serde_utils.py +2 -2
  89. flwr/common/telemetry.py +10 -6
  90. flwr/common/typing.py +65 -44
  91. flwr/compat/client/app.py +45 -47
  92. flwr/compat/client/grpc_client/connection.py +15 -14
  93. flwr/compat/common/constant.py +29 -0
  94. flwr/compat/server/app.py +6 -7
  95. flwr/proto/appio_pb2.py +13 -3
  96. flwr/proto/appio_pb2.pyi +134 -65
  97. flwr/proto/appio_pb2_grpc.py +20 -0
  98. flwr/proto/appio_pb2_grpc.pyi +27 -0
  99. flwr/proto/clientappio_pb2.py +17 -7
  100. flwr/proto/clientappio_pb2.pyi +15 -0
  101. flwr/proto/clientappio_pb2_grpc.py +206 -40
  102. flwr/proto/clientappio_pb2_grpc.pyi +168 -53
  103. flwr/proto/control_pb2.py +72 -40
  104. flwr/proto/control_pb2.pyi +319 -87
  105. flwr/proto/control_pb2_grpc.py +339 -28
  106. flwr/proto/control_pb2_grpc.pyi +209 -37
  107. flwr/proto/error_pb2.py +13 -3
  108. flwr/proto/error_pb2.pyi +24 -6
  109. flwr/proto/error_pb2_grpc.py +20 -0
  110. flwr/proto/error_pb2_grpc.pyi +27 -0
  111. flwr/proto/fab_pb2.py +24 -10
  112. flwr/proto/fab_pb2.pyi +68 -20
  113. flwr/proto/fab_pb2_grpc.py +20 -0
  114. flwr/proto/fab_pb2_grpc.pyi +27 -0
  115. flwr/proto/federation_pb2.py +38 -0
  116. flwr/proto/federation_pb2.pyi +56 -0
  117. flwr/proto/federation_pb2_grpc.py +24 -0
  118. flwr/proto/federation_pb2_grpc.pyi +31 -0
  119. flwr/proto/fleet_pb2.py +45 -27
  120. flwr/proto/fleet_pb2.pyi +190 -70
  121. flwr/proto/fleet_pb2_grpc.py +277 -66
  122. flwr/proto/fleet_pb2_grpc.pyi +201 -55
  123. flwr/proto/grpcadapter_pb2.py +14 -4
  124. flwr/proto/grpcadapter_pb2.pyi +38 -16
  125. flwr/proto/grpcadapter_pb2_grpc.py +35 -4
  126. flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
  127. flwr/proto/heartbeat_pb2.py +17 -7
  128. flwr/proto/heartbeat_pb2.pyi +51 -22
  129. flwr/proto/heartbeat_pb2_grpc.py +20 -0
  130. flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
  131. flwr/proto/log_pb2.py +13 -3
  132. flwr/proto/log_pb2.pyi +34 -11
  133. flwr/proto/log_pb2_grpc.py +20 -0
  134. flwr/proto/log_pb2_grpc.pyi +27 -0
  135. flwr/proto/message_pb2.py +15 -5
  136. flwr/proto/message_pb2.pyi +154 -86
  137. flwr/proto/message_pb2_grpc.py +20 -0
  138. flwr/proto/message_pb2_grpc.pyi +27 -0
  139. flwr/proto/node_pb2.py +16 -4
  140. flwr/proto/node_pb2.pyi +77 -4
  141. flwr/proto/node_pb2_grpc.py +20 -0
  142. flwr/proto/node_pb2_grpc.pyi +27 -0
  143. flwr/proto/recorddict_pb2.py +13 -3
  144. flwr/proto/recorddict_pb2.pyi +184 -107
  145. flwr/proto/recorddict_pb2_grpc.py +20 -0
  146. flwr/proto/recorddict_pb2_grpc.pyi +27 -0
  147. flwr/proto/run_pb2.py +40 -31
  148. flwr/proto/run_pb2.pyi +158 -84
  149. flwr/proto/run_pb2_grpc.py +20 -0
  150. flwr/proto/run_pb2_grpc.pyi +27 -0
  151. flwr/proto/serverappio_pb2.py +13 -3
  152. flwr/proto/serverappio_pb2.pyi +32 -8
  153. flwr/proto/serverappio_pb2_grpc.py +246 -65
  154. flwr/proto/serverappio_pb2_grpc.pyi +221 -85
  155. flwr/proto/simulationio_pb2.py +16 -8
  156. flwr/proto/simulationio_pb2.pyi +15 -0
  157. flwr/proto/simulationio_pb2_grpc.py +162 -41
  158. flwr/proto/simulationio_pb2_grpc.pyi +149 -55
  159. flwr/proto/transport_pb2.py +20 -10
  160. flwr/proto/transport_pb2.pyi +249 -160
  161. flwr/proto/transport_pb2_grpc.py +35 -4
  162. flwr/proto/transport_pb2_grpc.pyi +38 -8
  163. flwr/server/app.py +175 -128
  164. flwr/server/client_manager.py +4 -5
  165. flwr/server/client_proxy.py +10 -11
  166. flwr/server/compat/app.py +4 -5
  167. flwr/server/compat/app_utils.py +2 -1
  168. flwr/server/compat/grid_client_proxy.py +12 -13
  169. flwr/server/compat/legacy_context.py +3 -4
  170. flwr/server/fleet_event_log_interceptor.py +2 -1
  171. flwr/server/grid/grid.py +2 -3
  172. flwr/server/grid/grpc_grid.py +12 -10
  173. flwr/server/grid/inmemory_grid.py +4 -4
  174. flwr/server/run_serverapp.py +2 -3
  175. flwr/server/server.py +34 -39
  176. flwr/server/server_app.py +7 -8
  177. flwr/server/server_config.py +1 -2
  178. flwr/server/serverapp/app.py +34 -28
  179. flwr/server/serverapp_components.py +4 -5
  180. flwr/server/strategy/aggregate.py +9 -8
  181. flwr/server/strategy/bulyan.py +13 -11
  182. flwr/server/strategy/dp_adaptive_clipping.py +16 -20
  183. flwr/server/strategy/dp_fixed_clipping.py +12 -17
  184. flwr/server/strategy/dpfedavg_adaptive.py +3 -4
  185. flwr/server/strategy/dpfedavg_fixed.py +6 -10
  186. flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
  187. flwr/server/strategy/fedadagrad.py +18 -14
  188. flwr/server/strategy/fedadam.py +16 -14
  189. flwr/server/strategy/fedavg.py +16 -17
  190. flwr/server/strategy/fedavg_android.py +15 -15
  191. flwr/server/strategy/fedavgm.py +21 -18
  192. flwr/server/strategy/fedmedian.py +2 -3
  193. flwr/server/strategy/fedopt.py +11 -10
  194. flwr/server/strategy/fedprox.py +10 -9
  195. flwr/server/strategy/fedtrimmedavg.py +12 -11
  196. flwr/server/strategy/fedxgb_bagging.py +13 -11
  197. flwr/server/strategy/fedxgb_cyclic.py +6 -6
  198. flwr/server/strategy/fedxgb_nn_avg.py +4 -4
  199. flwr/server/strategy/fedyogi.py +16 -14
  200. flwr/server/strategy/krum.py +12 -11
  201. flwr/server/strategy/qfedavg.py +16 -15
  202. flwr/server/strategy/strategy.py +6 -9
  203. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +20 -9
  204. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
  205. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
  206. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
  207. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
  208. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +136 -42
  209. flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +28 -50
  210. flwr/server/superlink/fleet/message_handler/message_handler.py +141 -51
  211. flwr/server/superlink/fleet/rest_rere/rest_api.py +54 -33
  212. flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
  213. flwr/server/superlink/fleet/vce/backend/raybackend.py +6 -6
  214. flwr/server/superlink/fleet/vce/vce_api.py +32 -13
  215. flwr/server/superlink/linkstate/__init__.py +2 -0
  216. flwr/server/superlink/linkstate/in_memory_linkstate.py +293 -208
  217. flwr/server/superlink/linkstate/linkstate.py +176 -64
  218. flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
  219. flwr/server/superlink/linkstate/sql_linkstate.py +221 -0
  220. flwr/server/superlink/linkstate/sqlite_linkstate.py +743 -648
  221. flwr/server/superlink/linkstate/utils.py +11 -62
  222. flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
  223. flwr/server/superlink/serverappio/serverappio_servicer.py +28 -23
  224. flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
  225. flwr/server/superlink/simulation/simulationio_servicer.py +19 -14
  226. flwr/server/superlink/utils.py +4 -6
  227. flwr/server/typing.py +1 -1
  228. flwr/server/utils/tensorboard.py +15 -8
  229. flwr/server/utils/validator.py +2 -3
  230. flwr/server/workflow/default_workflows.py +7 -6
  231. flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
  232. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +13 -11
  233. flwr/serverapp/strategy/bulyan.py +16 -15
  234. flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
  235. flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
  236. flwr/serverapp/strategy/fedadagrad.py +10 -11
  237. flwr/serverapp/strategy/fedadam.py +10 -11
  238. flwr/serverapp/strategy/fedavg.py +10 -11
  239. flwr/serverapp/strategy/fedavgm.py +17 -16
  240. flwr/serverapp/strategy/fedmedian.py +2 -2
  241. flwr/serverapp/strategy/fedopt.py +10 -11
  242. flwr/serverapp/strategy/fedprox.py +7 -8
  243. flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
  244. flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
  245. flwr/serverapp/strategy/fedxgb_cyclic.py +10 -10
  246. flwr/serverapp/strategy/fedyogi.py +9 -11
  247. flwr/serverapp/strategy/krum.py +7 -7
  248. flwr/serverapp/strategy/multikrum.py +9 -9
  249. flwr/serverapp/strategy/qfedavg.py +17 -16
  250. flwr/serverapp/strategy/strategy.py +6 -9
  251. flwr/serverapp/strategy/strategy_utils.py +7 -8
  252. flwr/simulation/app.py +46 -42
  253. flwr/simulation/legacy_app.py +12 -12
  254. flwr/simulation/ray_transport/ray_actor.py +11 -12
  255. flwr/simulation/ray_transport/ray_client_proxy.py +14 -19
  256. flwr/simulation/run_simulation.py +46 -44
  257. flwr/simulation/simulationio_connection.py +4 -4
  258. flwr/{common → supercore}/address.py +1 -37
  259. flwr/supercore/cli/flower_superexec.py +3 -4
  260. flwr/supercore/constant.py +69 -0
  261. flwr/supercore/corestate/corestate.py +24 -3
  262. flwr/supercore/corestate/in_memory_corestate.py +138 -0
  263. flwr/supercore/corestate/sql_corestate.py +153 -0
  264. flwr/supercore/corestate/sqlite_corestate.py +157 -0
  265. flwr/supercore/credential_store/__init__.py +33 -0
  266. flwr/supercore/credential_store/credential_store.py +34 -0
  267. flwr/supercore/credential_store/file_credential_store.py +76 -0
  268. flwr/{common → supercore}/date.py +0 -11
  269. flwr/supercore/ffs/disk_ffs.py +1 -2
  270. flwr/supercore/ffs/ffs.py +1 -2
  271. flwr/supercore/ffs/ffs_factory.py +1 -2
  272. flwr/{common → supercore}/heartbeat.py +20 -25
  273. flwr/supercore/object_store/in_memory_object_store.py +1 -6
  274. flwr/supercore/object_store/object_store.py +1 -2
  275. flwr/supercore/object_store/object_store_factory.py +27 -8
  276. flwr/supercore/object_store/sqlite_object_store.py +253 -0
  277. flwr/{cli/new/templates/app → supercore/primitives}/__init__.py +1 -1
  278. flwr/supercore/primitives/asymmetric.py +117 -0
  279. flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
  280. flwr/supercore/sql_mixin.py +292 -0
  281. flwr/supercore/sqlite_mixin.py +156 -0
  282. flwr/{client/clientapp → supercore/state}/__init__.py +2 -2
  283. flwr/supercore/state/schema/README.md +125 -0
  284. flwr/{cli/new/templates → supercore/state/schema}/__init__.py +2 -2
  285. flwr/supercore/state/schema/corestate_tables.py +36 -0
  286. flwr/supercore/state/schema/linkstate_tables.py +152 -0
  287. flwr/supercore/state/schema/objectstore_tables.py +90 -0
  288. flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
  289. flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
  290. flwr/supercore/superexec/run_superexec.py +9 -13
  291. flwr/supercore/utils.py +224 -0
  292. flwr/superlink/artifact_provider/artifact_provider.py +1 -2
  293. flwr/superlink/auth_plugin/__init__.py +5 -2
  294. flwr/superlink/auth_plugin/auth_plugin.py +20 -19
  295. flwr/superlink/auth_plugin/noop_auth_plugin.py +84 -0
  296. flwr/superlink/federation/__init__.py +24 -0
  297. flwr/superlink/federation/federation_manager.py +64 -0
  298. flwr/superlink/federation/noop_federation_manager.py +71 -0
  299. flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +41 -32
  300. flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
  301. flwr/superlink/servicer/control/control_grpc.py +20 -17
  302. flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
  303. flwr/superlink/servicer/control/control_servicer.py +328 -68
  304. flwr/supernode/cli/flower_supernode.py +74 -26
  305. flwr/supernode/nodestate/in_memory_nodestate.py +121 -49
  306. flwr/supernode/nodestate/nodestate.py +52 -8
  307. flwr/supernode/nodestate/nodestate_factory.py +7 -4
  308. flwr/supernode/runtime/run_clientapp.py +43 -24
  309. flwr/supernode/servicer/clientappio/clientappio_servicer.py +48 -10
  310. flwr/supernode/start_client_internal.py +185 -57
  311. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/METADATA +10 -11
  312. flwr_nightly-1.26.0.dev20260121.dist-info/RECORD +411 -0
  313. flwr/cli/new/templates/app/.gitignore.tpl +0 -163
  314. flwr/cli/new/templates/app/LICENSE.tpl +0 -202
  315. flwr/cli/new/templates/app/README.baseline.md.tpl +0 -127
  316. flwr/cli/new/templates/app/README.flowertune.md.tpl +0 -68
  317. flwr/cli/new/templates/app/README.md.tpl +0 -37
  318. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +0 -1
  319. flwr/cli/new/templates/app/code/__init__.py.tpl +0 -1
  320. flwr/cli/new/templates/app/code/__init__.pytorch_legacy_api.py.tpl +0 -1
  321. flwr/cli/new/templates/app/code/client.baseline.py.tpl +0 -75
  322. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +0 -93
  323. flwr/cli/new/templates/app/code/client.jax.py.tpl +0 -71
  324. flwr/cli/new/templates/app/code/client.mlx.py.tpl +0 -102
  325. flwr/cli/new/templates/app/code/client.numpy.py.tpl +0 -46
  326. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +0 -80
  327. flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +0 -55
  328. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +0 -108
  329. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +0 -82
  330. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +0 -110
  331. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +0 -36
  332. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +0 -92
  333. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +0 -87
  334. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +0 -56
  335. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +0 -73
  336. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +0 -78
  337. flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -66
  338. flwr/cli/new/templates/app/code/server.baseline.py.tpl +0 -43
  339. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +0 -42
  340. flwr/cli/new/templates/app/code/server.jax.py.tpl +0 -39
  341. flwr/cli/new/templates/app/code/server.mlx.py.tpl +0 -41
  342. flwr/cli/new/templates/app/code/server.numpy.py.tpl +0 -38
  343. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +0 -41
  344. flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +0 -31
  345. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +0 -44
  346. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +0 -38
  347. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +0 -56
  348. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +0 -1
  349. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +0 -98
  350. flwr/cli/new/templates/app/code/task.jax.py.tpl +0 -57
  351. flwr/cli/new/templates/app/code/task.mlx.py.tpl +0 -102
  352. flwr/cli/new/templates/app/code/task.numpy.py.tpl +0 -7
  353. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +0 -98
  354. flwr/cli/new/templates/app/code/task.pytorch_legacy_api.py.tpl +0 -111
  355. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +0 -67
  356. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +0 -52
  357. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +0 -67
  358. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +0 -1
  359. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +0 -146
  360. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +0 -80
  361. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +0 -65
  362. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +0 -52
  363. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +0 -56
  364. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +0 -49
  365. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +0 -53
  366. flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +0 -53
  367. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +0 -52
  368. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +0 -53
  369. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +0 -61
  370. flwr/common/pyproject.py +0 -42
  371. flwr/supercore/object_store/utils.py +0 -43
  372. flwr_nightly-1.23.0.dev20250930.dist-info/RECORD +0 -429
  373. /flwr/{common → supercore}/version.py +0 -0
  374. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/WHEEL +0 -0
  375. {flwr_nightly-1.23.0.dev20250930.dist-info → flwr_nightly-1.26.0.dev20260121.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,84 @@
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
+ """Concrete NoOp implementation for Servicer-side account authentication and
16
+ authorization plugins."""
17
+
18
+
19
+ from collections.abc import Sequence
20
+ from pathlib import Path
21
+
22
+ from flwr.common.constant import NOOP_ACCOUNT_NAME, NOOP_FLWR_AID, AuthnType
23
+ from flwr.common.typing import (
24
+ AccountAuthCredentials,
25
+ AccountAuthLoginDetails,
26
+ AccountInfo,
27
+ )
28
+
29
+ from .auth_plugin import ControlAuthnPlugin, ControlAuthzPlugin
30
+
31
+ NOOP_ACCOUNT_INFO = AccountInfo(
32
+ flwr_aid=NOOP_FLWR_AID,
33
+ account_name=NOOP_ACCOUNT_NAME,
34
+ )
35
+
36
+
37
+ class NoOpControlAuthnPlugin(ControlAuthnPlugin):
38
+ """No-operation implementation of ControlAuthnPlugin."""
39
+
40
+ def __init__(
41
+ self,
42
+ account_auth_config_path: Path,
43
+ verify_tls_cert: bool,
44
+ ):
45
+ pass
46
+
47
+ def get_login_details(self) -> AccountAuthLoginDetails | None:
48
+ """Get the login details."""
49
+ # This allows the `flwr login` command to load the NoOp plugin accordingly,
50
+ # which then raises a LoginError when attempting to login.
51
+ return AccountAuthLoginDetails(
52
+ authn_type=AuthnType.NOOP, # No operation authn type
53
+ device_code="",
54
+ verification_uri_complete="",
55
+ expires_in=0,
56
+ interval=0,
57
+ )
58
+
59
+ def validate_tokens_in_metadata(
60
+ self, metadata: Sequence[tuple[str, str | bytes]]
61
+ ) -> tuple[bool, AccountInfo | None]:
62
+ """Return valid for no-op plugin."""
63
+ return True, NOOP_ACCOUNT_INFO
64
+
65
+ def get_auth_tokens(self, device_code: str) -> AccountAuthCredentials | None:
66
+ """Get authentication tokens."""
67
+ raise RuntimeError("NoOp plugin does not support getting auth tokens.")
68
+
69
+ def refresh_tokens(
70
+ self, metadata: Sequence[tuple[str, str | bytes]]
71
+ ) -> tuple[Sequence[tuple[str, str | bytes]] | None, AccountInfo | None]:
72
+ """Refresh authentication tokens in the provided metadata."""
73
+ return metadata, NOOP_ACCOUNT_INFO
74
+
75
+
76
+ class NoOpControlAuthzPlugin(ControlAuthzPlugin):
77
+ """No-operation implementation of ControlAuthzPlugin."""
78
+
79
+ def __init__(self, account_auth_config_path: Path, verify_tls_cert: bool):
80
+ pass
81
+
82
+ def authorize(self, account_info: AccountInfo) -> bool:
83
+ """Return True for no-op plugin."""
84
+ return True
@@ -0,0 +1,24 @@
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
+ """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
 
@@ -31,34 +32,42 @@ from flwr.proto.control_pb2 import ( # pylint: disable=E0611
31
32
  StreamLogsRequest,
32
33
  StreamLogsResponse,
33
34
  )
34
- from flwr.superlink.auth_plugin import ControlAuthPlugin, ControlAuthzPlugin
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
 
53
- class ControlUserAuthInterceptor(grpc.ServerInterceptor): # type: ignore
54
- """Control API interceptor for user authentication."""
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
+
62
+ class ControlAccountAuthInterceptor(grpc.ServerInterceptor): # type: ignore
63
+ """Control API interceptor for account authentication."""
55
64
 
56
65
  def __init__(
57
66
  self,
58
- auth_plugin: ControlAuthPlugin,
67
+ authn_plugin: ControlAuthnPlugin,
59
68
  authz_plugin: ControlAuthzPlugin,
60
69
  ):
61
- self.auth_plugin = auth_plugin
70
+ self.authn_plugin = authn_plugin
62
71
  self.authz_plugin = authz_plugin
63
72
 
64
73
  def intercept_service(
@@ -93,48 +102,48 @@ class ControlUserAuthInterceptor(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
- # For other requests, check if the user is authenticated
100
- valid_tokens, account_info = self.auth_plugin.validate_tokens_in_metadata(
108
+ # For other requests, check if the account is authenticated
109
+ valid_tokens, account_info = self.authn_plugin.validate_tokens_in_metadata(
101
110
  metadata
102
111
  )
103
112
  if valid_tokens:
104
113
  if account_info is None:
105
114
  context.abort(
106
115
  grpc.StatusCode.UNAUTHENTICATED,
107
- "Tokens validated, but user info not found",
116
+ "Tokens validated, but account info not found",
108
117
  )
109
118
  raise grpc.RpcError()
110
- # Store user info in contextvars for authenticated users
119
+ # Store account info in contextvars for authenticated accounts
111
120
  shared_account_info.set(account_info)
112
- # Check if the user is authorized
113
- if not self.authz_plugin.verify_user_authorization(account_info):
121
+ # Check if the account is authorized
122
+ if not self.authz_plugin.authorize(account_info):
114
123
  context.abort(
115
124
  grpc.StatusCode.PERMISSION_DENIED,
116
- "❗️ User not authorized. "
125
+ "❗️ Account not authorized. "
117
126
  "Please contact the SuperLink administrator.",
118
127
  )
119
128
  raise grpc.RpcError()
120
129
  return call(request, context) # type: ignore
121
130
 
122
- # If the user is not authenticated, refresh tokens
123
- tokens, account_info = self.auth_plugin.refresh_tokens(metadata)
131
+ # If the account is not authenticated, refresh tokens
132
+ tokens, account_info = self.authn_plugin.refresh_tokens(metadata)
124
133
  if tokens is not None:
125
134
  if account_info is None:
126
135
  context.abort(
127
136
  grpc.StatusCode.UNAUTHENTICATED,
128
- "Tokens refreshed, but user info not found",
137
+ "Tokens refreshed, but account info not found",
129
138
  )
130
139
  raise grpc.RpcError()
131
- # Store user info in contextvars for authenticated users
140
+ # Store account info in contextvars for authenticated accounts
132
141
  shared_account_info.set(account_info)
133
- # Check if the user is authorized
134
- if not self.authz_plugin.verify_user_authorization(account_info):
142
+ # Check if the account is authorized
143
+ if not self.authz_plugin.authorize(account_info):
135
144
  context.abort(
136
145
  grpc.StatusCode.PERMISSION_DENIED,
137
- "❗️ User not authorized. "
146
+ "❗️ Account not authorized. "
138
147
  "Please contact the SuperLink administrator.",
139
148
  )
140
149
  raise grpc.RpcError()
@@ -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_user_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
 
@@ -31,18 +30,22 @@ from flwr.supercore.ffs import FfsFactory
31
30
  from flwr.supercore.license_plugin import LicensePlugin
32
31
  from flwr.supercore.object_store import ObjectStoreFactory
33
32
  from flwr.superlink.artifact_provider import ArtifactProvider
34
- from flwr.superlink.auth_plugin import ControlAuthPlugin, ControlAuthzPlugin
33
+ from flwr.superlink.auth_plugin import (
34
+ ControlAuthnPlugin,
35
+ ControlAuthzPlugin,
36
+ NoOpControlAuthnPlugin,
37
+ )
35
38
 
39
+ from .control_account_auth_interceptor import ControlAccountAuthInterceptor
36
40
  from .control_event_log_interceptor import ControlEventLogInterceptor
37
41
  from .control_license_interceptor import ControlLicenseInterceptor
38
42
  from .control_servicer import ControlServicer
39
- from .control_user_auth_interceptor import ControlUserAuthInterceptor
40
43
 
41
44
  try:
42
45
  from flwr.ee import get_license_plugin
43
46
  except ImportError:
44
47
 
45
- def get_license_plugin() -> Optional[LicensePlugin]:
48
+ def get_license_plugin() -> LicensePlugin | None:
46
49
  """Return the license plugin."""
47
50
 
48
51
 
@@ -52,15 +55,16 @@ def run_control_api_grpc(
52
55
  state_factory: LinkStateFactory,
53
56
  ffs_factory: FfsFactory,
54
57
  objectstore_factory: ObjectStoreFactory,
55
- certificates: Optional[tuple[bytes, bytes, bytes]],
58
+ certificates: tuple[bytes, bytes, bytes] | None,
56
59
  is_simulation: bool,
57
- auth_plugin: Optional[ControlAuthPlugin] = None,
58
- authz_plugin: Optional[ControlAuthzPlugin] = None,
59
- event_log_plugin: Optional[EventLogWriterPlugin] = None,
60
- artifact_provider: Optional[ArtifactProvider] = None,
60
+ authn_plugin: ControlAuthnPlugin,
61
+ authz_plugin: ControlAuthzPlugin,
62
+ event_log_plugin: EventLogWriterPlugin | None = None,
63
+ artifact_provider: ArtifactProvider | None = None,
64
+ fleet_api_type: str | None = None,
61
65
  ) -> grpc.Server:
62
66
  """Run Control API (gRPC, request-response)."""
63
- license_plugin: Optional[LicensePlugin] = get_license_plugin()
67
+ license_plugin: LicensePlugin | None = get_license_plugin()
64
68
  if license_plugin and not license_plugin.check_license():
65
69
  flwr_exit(ExitCode.SUPERLINK_LICENSE_INVALID)
66
70
 
@@ -69,15 +73,14 @@ def run_control_api_grpc(
69
73
  ffs_factory=ffs_factory,
70
74
  objectstore_factory=objectstore_factory,
71
75
  is_simulation=is_simulation,
72
- auth_plugin=auth_plugin,
76
+ authn_plugin=authn_plugin,
73
77
  artifact_provider=artifact_provider,
78
+ fleet_api_type=fleet_api_type,
74
79
  )
75
- interceptors: list[grpc.ServerInterceptor] = []
80
+ interceptors = [ControlAccountAuthInterceptor(authn_plugin, authz_plugin)]
76
81
  if license_plugin is not None:
77
82
  interceptors.append(ControlLicenseInterceptor(license_plugin))
78
- if auth_plugin is not None and authz_plugin is not None:
79
- interceptors.append(ControlUserAuthInterceptor(auth_plugin, authz_plugin))
80
- # Event log interceptor must be added after user auth interceptor
83
+ # Event log interceptor must be added after account auth interceptor
81
84
  if event_log_plugin is not None:
82
85
  interceptors.append(ControlEventLogInterceptor(event_log_plugin))
83
86
  log(INFO, "Flower event logging enabled")
@@ -90,12 +93,12 @@ def run_control_api_grpc(
90
93
  interceptors=interceptors or None,
91
94
  )
92
95
 
93
- if auth_plugin is None:
96
+ if isinstance(authn_plugin, NoOpControlAuthnPlugin):
94
97
  log(INFO, "Flower Deployment Runtime: Starting Control API on %s", address)
95
98
  else:
96
99
  log(
97
100
  INFO,
98
- "Flower Deployment Runtime: Starting Control API with user "
101
+ "Flower Deployment Runtime: Starting Control API with account "
99
102
  "authentication on %s",
100
103
  address,
101
104
  )
@@ -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