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
flwr/__init__.py CHANGED
@@ -15,18 +15,29 @@
15
15
  """Flower main package."""
16
16
 
17
17
 
18
- from flwr.common.version import package_version as _package_version
18
+ import importlib
19
19
 
20
- from . import app, client, clientapp, common, server, serverapp, simulation
20
+ from flwr.supercore.version import package_version as _package_version
21
+
22
+ from . import app, clientapp, serverapp
21
23
 
22
24
  __all__ = [
23
25
  "app",
24
- "client",
25
26
  "clientapp",
26
- "common",
27
- "server",
28
27
  "serverapp",
29
- "simulation",
30
28
  ]
31
29
 
32
30
  __version__ = _package_version
31
+
32
+
33
+ # Lazy imports for legacy support
34
+ _lazy_imports = {"simulation", "server", "client", "common"}
35
+
36
+
37
+ def __getattr__(name: str) -> object:
38
+ """Lazy import for legacy support."""
39
+ if name in _lazy_imports:
40
+ module = importlib.import_module(f"{__name__}.{name}")
41
+ globals()[name] = module
42
+ return module
43
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
flwr/app/__init__.py CHANGED
@@ -15,7 +15,6 @@
15
15
  """Public Flower App APIs."""
16
16
 
17
17
 
18
- from flwr.common.constant import MessageType
19
18
  from flwr.common.context import Context
20
19
  from flwr.common.message import Message
21
20
  from flwr.common.record import (
@@ -27,7 +26,9 @@ from flwr.common.record import (
27
26
  )
28
27
 
29
28
  from .error import Error
29
+ from .message_type import MessageType
30
30
  from .metadata import Metadata
31
+ from .user_config import UserConfig, UserConfigValue
31
32
 
32
33
  __all__ = [
33
34
  "Array",
@@ -40,4 +41,6 @@ __all__ = [
40
41
  "Metadata",
41
42
  "MetricRecord",
42
43
  "RecordDict",
44
+ "UserConfig",
45
+ "UserConfigValue",
43
46
  ]
flwr/app/error.py CHANGED
@@ -17,7 +17,7 @@
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- from typing import Optional, cast
20
+ from typing import cast
21
21
 
22
22
  DEFAULT_TTL = 43200 # This is 12 hours
23
23
  MESSAGE_INIT_ERROR_MESSAGE = (
@@ -54,7 +54,7 @@ class Error:
54
54
  @property
55
55
  def reason(self) -> str | None:
56
56
  """Reason reported about the error."""
57
- return cast(Optional[str], self.__dict__["_reason"])
57
+ return cast(str | None, self.__dict__["_reason"])
58
58
 
59
59
  def __repr__(self) -> str:
60
60
  """Return a string representation of this instance."""
flwr/app/exception.py CHANGED
@@ -15,11 +15,11 @@
15
15
  """Flower application exceptions."""
16
16
 
17
17
 
18
- class AppExitException(BaseException):
18
+ class AppExitException(Exception):
19
19
  """Base exception for all application-level errors in ServerApp and ClientApp.
20
20
 
21
- When raised, the process will exit and report a telemetry event with the associated
22
- exit code. This is not intended to be caught by user code.
21
+ When raised (and not suppressed), the process will exit and report a telemetry event
22
+ with the associated exit code.
23
23
  """
24
24
 
25
25
  # Default exit code — subclasses must override
@@ -0,0 +1,29 @@
1
+ # Copyright 2026 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
+ """MessageType constants."""
16
+
17
+ from __future__ import annotations
18
+
19
+
20
+ class MessageType:
21
+ """Message type."""
22
+
23
+ TRAIN = "train"
24
+ EVALUATE = "evaluate"
25
+ QUERY = "query"
26
+
27
+ def __new__(cls) -> MessageType:
28
+ """Prevent instantiation."""
29
+ raise TypeError(f"{cls.__name__} cannot be instantiated.")
flwr/app/metadata.py CHANGED
@@ -19,7 +19,10 @@ from __future__ import annotations
19
19
 
20
20
  from typing import cast
21
21
 
22
- from ..common.constant import MessageType, MessageTypeLegacy
22
+ from flwr.app.message_type import MessageType
23
+ from flwr.supercore.constant import SYSTEM_MESSAGE_TYPE
24
+
25
+ from ..common.constant import MessageTypeLegacy
23
26
 
24
27
 
25
28
  class Metadata: # pylint: disable=too-many-instance-attributes
@@ -194,7 +197,7 @@ def validate_message_type(message_type: str) -> bool:
194
197
  MessageType.TRAIN,
195
198
  MessageType.EVALUATE,
196
199
  MessageType.QUERY,
197
- MessageType.SYSTEM,
200
+ SYSTEM_MESSAGE_TYPE,
198
201
  }
199
202
  if message_type in valid_types:
200
203
  return True
@@ -0,0 +1,19 @@
1
+ # Copyright 2026 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
+ """UserConfig type definition."""
16
+
17
+
18
+ UserConfigValue = bool | float | int | str
19
+ UserConfig = dict[str, UserConfigValue]
flwr/cli/app.py CHANGED
@@ -14,12 +14,20 @@
14
14
  # ==============================================================================
15
15
  """Flower command line interface."""
16
16
 
17
+
18
+ from typing import Any, TypedDict
19
+
17
20
  import typer
18
21
  from typer.main import get_command
19
22
 
20
- from flwr.common.version import package_version
23
+ from flwr.supercore.version import package_version
21
24
 
25
+ from .app_cmd import publish as app_publish
26
+ from .app_cmd import review as app_review
22
27
  from .build import build
28
+ from .config import ls as config_list
29
+ from .federation import ls as federation_list
30
+ from .flower_config import init_flwr_config
23
31
  from .install import install
24
32
  from .log import log
25
33
  from .login import login
@@ -28,6 +36,18 @@ from .new import new
28
36
  from .pull import pull
29
37
  from .run import run
30
38
  from .stop import stop
39
+ from .supernode import ls as supernode_list
40
+ from .supernode import register as supernode_register
41
+ from .supernode import unregister as supernode_unregister
42
+
43
+
44
+ class CommandKwargs(TypedDict):
45
+ """Keywords for typer command to make mypy happy."""
46
+
47
+ context_settings: dict[str, Any]
48
+
49
+
50
+ ALLOW_EXTRAS: CommandKwargs = {"context_settings": {"allow_extra_args": True}}
31
51
 
32
52
  app = typer.Typer(
33
53
  help=typer.style(
@@ -43,18 +63,50 @@ app.command()(new)
43
63
  app.command()(run)
44
64
  app.command()(build)
45
65
  app.command()(install)
46
- app.command()(log)
47
- app.command()(ls)
48
- app.command()(stop)
66
+ app.command(**ALLOW_EXTRAS)(log)
67
+ app.command("list", **ALLOW_EXTRAS)(ls)
68
+ app.command(hidden=True, **ALLOW_EXTRAS)(ls)
69
+ app.command(**ALLOW_EXTRAS)(stop)
49
70
  app.command()(login)
50
- app.command()(pull)
71
+ app.command(**ALLOW_EXTRAS)(pull)
72
+
73
+ # Create supernode command group
74
+ supernode_app = typer.Typer(help="Manage SuperNodes")
75
+ supernode_app.command()(supernode_register)
76
+ supernode_app.command()(supernode_unregister)
77
+ # Make it appear as "list"
78
+ supernode_app.command("list")(supernode_list)
79
+ # Hide "ls" command (left as alias)
80
+ supernode_app.command(hidden=True)(supernode_list)
81
+ app.add_typer(supernode_app, name="supernode")
82
+
83
+ # Create app command group
84
+ app_app = typer.Typer(help="Manage Apps")
85
+ app_app.command()(app_review)
86
+ app_app.command()(app_publish)
87
+ app.add_typer(app_app, name="app")
88
+
89
+ # Create federation command group
90
+ federation_app = typer.Typer(help="Manage Federations")
91
+ # Make it appear as "list"
92
+ federation_app.command("list")(federation_list)
93
+ # Hide "ls" command (left as alias)
94
+ federation_app.command(hidden=True)(federation_list)
95
+ app.add_typer(federation_app, name="federation")
96
+
97
+ # Create config command group
98
+ config_app = typer.Typer(help="Manage Configuration")
99
+ config_app.command("list")(config_list)
100
+ # Hide "ls" command (left as alias)
101
+ config_app.command(hidden=True)(config_list)
102
+ app.add_typer(config_app, name="config")
51
103
 
52
104
  typer_click_object = get_command(app)
53
105
 
54
106
 
55
107
  @app.callback(invoke_without_command=True)
56
- def version_callback(
57
- ver: bool = typer.Option(
108
+ def main(
109
+ version: bool = typer.Option(
58
110
  None,
59
111
  "-V",
60
112
  "--version",
@@ -62,8 +114,9 @@ def version_callback(
62
114
  help="Show the version and exit.",
63
115
  ),
64
116
  ) -> None:
65
- """Print version."""
66
- if ver:
117
+ """Flower CLI."""
118
+ init_flwr_config()
119
+ if version:
67
120
  typer.secho(f"Flower version: {package_version}", fg="blue")
68
121
  raise typer.Exit()
69
122
 
@@ -12,4 +12,12 @@
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 / code templates."""
15
+ """Flower command line interface `app` command."""
16
+
17
+ from .publish import publish as publish
18
+ from .review import review as review
19
+
20
+ __all__ = [
21
+ "publish",
22
+ "review",
23
+ ]
@@ -0,0 +1,285 @@
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
+ """Flower command line interface `app publish` command."""
16
+
17
+
18
+ from contextlib import ExitStack
19
+ from pathlib import Path
20
+ from typing import IO, Annotated
21
+
22
+ import requests
23
+ import typer
24
+ from requests import Response
25
+
26
+ from flwr.common.constant import FAB_CONFIG_FILE
27
+ from flwr.supercore.constant import (
28
+ APP_PUBLISH_EXCLUDE_PATTERNS,
29
+ APP_PUBLISH_INCLUDE_PATTERNS,
30
+ MAX_DIR_DEPTH,
31
+ MAX_FILE_BYTES,
32
+ MAX_FILE_COUNT,
33
+ MAX_TOTAL_BYTES,
34
+ MIME_MAP,
35
+ PLATFORM_API_URL,
36
+ UTF8,
37
+ )
38
+ from flwr.supercore.version import package_version as flwr_version
39
+
40
+ from ..auth_plugin.oidc_cli_plugin import OidcCliPlugin
41
+ from ..config_utils import (
42
+ load_and_validate,
43
+ process_loaded_project_config,
44
+ validate_federation_in_project_config,
45
+ )
46
+ from ..constant import FEDERATION_CONFIG_HELP_MESSAGE
47
+ from ..utils import build_pathspec, load_cli_auth_plugin, load_gitignore_patterns
48
+
49
+
50
+ # pylint: disable=too-many-locals
51
+ def publish(
52
+ app: Annotated[
53
+ Path,
54
+ typer.Argument(
55
+ help="Project directory to upload (defaults to current directory)."
56
+ ),
57
+ ] = Path("."),
58
+ federation: Annotated[
59
+ str | None,
60
+ typer.Argument(
61
+ help="Name of the federation used for login before publishing app."
62
+ ),
63
+ ] = None,
64
+ federation_config_overrides: Annotated[
65
+ list[str] | None,
66
+ typer.Option(
67
+ "--federation-config",
68
+ help=FEDERATION_CONFIG_HELP_MESSAGE,
69
+ ),
70
+ ] = None,
71
+ ) -> None:
72
+ """Publish a Flower App to the Flower Platform.
73
+
74
+ This command uploads your app project to the Flower Platform. Files are filtered
75
+ based on .gitignore patterns and allowed file extensions.
76
+ """
77
+ # Load configs
78
+ pyproject_path = app / FAB_CONFIG_FILE if app else None
79
+ config, errors, warnings = load_and_validate(pyproject_path, check_module=False)
80
+ config = process_loaded_project_config(config, errors, warnings)
81
+ federation, federation_config = validate_federation_in_project_config(
82
+ federation, config, federation_config_overrides
83
+ )
84
+
85
+ # Load the authentication plugin
86
+ auth_plugin = load_cli_auth_plugin(app, federation, federation_config)
87
+ auth_plugin.load_tokens()
88
+ if not isinstance(auth_plugin, OidcCliPlugin) or not auth_plugin.access_token:
89
+ typer.secho(
90
+ "❌ Please log in before publishing app.",
91
+ fg=typer.colors.RED,
92
+ err=True,
93
+ )
94
+ raise typer.Exit(code=1)
95
+
96
+ # Load token from the plugin
97
+ token = auth_plugin.access_token
98
+
99
+ # Collect & validate app files
100
+ file_paths = _collect_file_paths(app)
101
+ _validate_files(file_paths)
102
+
103
+ # Build and POST multipart
104
+ with ExitStack() as stack:
105
+ files_param = _build_multipart_files_param(app, file_paths, stack)
106
+ try:
107
+ resp = _post_files(files_param, token)
108
+ except requests.RequestException as err:
109
+ typer.secho(f"❌ Network error: {err}", fg=typer.colors.RED, err=True)
110
+ raise typer.Exit(code=1) from err
111
+
112
+ if resp.ok:
113
+ typer.secho("🎊 Upload successful", fg=typer.colors.GREEN, bold=True)
114
+ return # success
115
+
116
+ # Error path:
117
+ msg = f"❌ Upload failed with status {resp.status_code}"
118
+ if resp.text:
119
+ msg += f": {resp.text}"
120
+ typer.secho(msg, fg=typer.colors.RED, err=True)
121
+ raise typer.Exit(code=1)
122
+
123
+
124
+ def _depth_of(relative_path_to_root: Path) -> int:
125
+ """Return depth that is number of parts (directories) in the relative path
126
+ (excluding filename).
127
+
128
+ Example: "a/b/c.py" -> depth 2
129
+ Interpret "directory depth" as number of directories: len(parts) - 1
130
+ """
131
+ return max(0, len(relative_path_to_root.parts) - 1)
132
+
133
+
134
+ def _detect_mime(path: Path) -> str:
135
+ """Detect files' MIME."""
136
+ return MIME_MAP.get(path.suffix.lower(), "text/plain; charset=utf-8")
137
+
138
+
139
+ def _collect_file_paths(root: Path) -> list[Path]:
140
+ """Return list of file paths that match include/exclude patterns."""
141
+ # Build include/exclude pathspecs
142
+ # Note: This should be a temporary solution until we have a complete mechanism
143
+ # for configurable inclusion and exclusion rules.
144
+ # Note: Unlike Git, we do not support nested .gitignore files in subdirectories.
145
+ gitignore_patterns = tuple(load_gitignore_patterns(root / ".gitignore"))
146
+ exclude_pathspec = build_pathspec(gitignore_patterns + APP_PUBLISH_EXCLUDE_PATTERNS)
147
+ include_pathspec = build_pathspec(APP_PUBLISH_INCLUDE_PATTERNS)
148
+
149
+ # Walk the directory tree
150
+ file_paths: list[Path] = []
151
+ for path in root.rglob("*"):
152
+ if not path.is_file():
153
+ continue
154
+
155
+ # Skip excluded or not included files
156
+ # Note: pathspec requires POSIX style relative paths
157
+ relative_path = path.relative_to(root)
158
+ posix = relative_path.as_posix()
159
+ if exclude_pathspec.match_file(posix) or not include_pathspec.match_file(posix):
160
+ typer.echo(typer.style(f"Skip: {path}", fg=typer.colors.YELLOW))
161
+ continue
162
+
163
+ # Check max depth
164
+ if _depth_of(relative_path) > MAX_DIR_DEPTH:
165
+ typer.secho(
166
+ f"Error: '{path}' "
167
+ f"exceeds the maximum directory depth "
168
+ f"of {MAX_DIR_DEPTH}.",
169
+ fg=typer.colors.RED,
170
+ err=True,
171
+ )
172
+ raise typer.Exit(code=2)
173
+
174
+ file_paths.append(path)
175
+
176
+ # Sort for deterministic ordering
177
+ file_paths.sort(key=lambda path: path.as_posix())
178
+ return file_paths
179
+
180
+
181
+ def _validate_files(file_paths: list[Path]) -> None:
182
+ """Validate files against upload constraints.
183
+
184
+ Checks file count, individual file size, total size, and UTF-8 encoding.
185
+ """
186
+ if len(file_paths) == 0:
187
+ typer.secho(
188
+ "Nothing to upload: no files matched after applying .gitignore and "
189
+ "allowed extensions.",
190
+ fg=typer.colors.RED,
191
+ err=True,
192
+ )
193
+ raise typer.Exit(code=2)
194
+
195
+ if len(file_paths) > MAX_FILE_COUNT:
196
+ typer.secho(
197
+ f"Too many files: {len(file_paths)} > allowed maximum of {MAX_FILE_COUNT}.",
198
+ fg=typer.colors.RED,
199
+ err=True,
200
+ )
201
+ raise typer.Exit(code=2)
202
+
203
+ # Calculate files size
204
+ total_size = 0
205
+ for path in file_paths:
206
+ file_size = path.stat().st_size
207
+ total_size += file_size
208
+
209
+ # Check single file size
210
+ if file_size > MAX_FILE_BYTES:
211
+ typer.secho(
212
+ f"File too large: '{path.as_posix()}' is {file_size:,} bytes, "
213
+ f"exceeding the per-file limit of {MAX_FILE_BYTES:,} bytes.",
214
+ fg=typer.colors.RED,
215
+ err=True,
216
+ )
217
+ raise typer.Exit(code=2)
218
+
219
+ # Ensure we can decode as UTF-8.
220
+ try:
221
+ path.read_text(encoding=UTF8)
222
+ except UnicodeDecodeError as err:
223
+ typer.secho(
224
+ f"Encoding error: '{path}' is not UTF-8 encoded.",
225
+ fg=typer.colors.RED,
226
+ err=True,
227
+ )
228
+ raise typer.Exit(code=2) from err
229
+
230
+ # Check total files size
231
+ if total_size > MAX_TOTAL_BYTES:
232
+ typer.secho(
233
+ "Total size of all files is too large: "
234
+ f"{total_size:,} bytes > {MAX_TOTAL_BYTES:,} bytes.",
235
+ fg=typer.colors.RED,
236
+ err=True,
237
+ )
238
+ raise typer.Exit(code=2)
239
+
240
+ # Print validation passed prompt
241
+ typer.echo(typer.style("✅ Validation passed", fg=typer.colors.GREEN, bold=True))
242
+ typer.echo(f"{len(file_paths)} files, {total_size:,} bytes in total")
243
+
244
+
245
+ def _build_multipart_files_param(
246
+ root: Path,
247
+ file_paths: list[Path],
248
+ stack: ExitStack,
249
+ ) -> list[tuple[str, tuple[str, IO[bytes], str]]]:
250
+ """Build multipart/form-data files parameter for HTTP upload.
251
+
252
+ Returns list of tuples: (field_name, (filename, file_object, content_type)).
253
+ File handles are registered with ExitStack for proper cleanup.
254
+ """
255
+ form: list[tuple[str, tuple[str, IO[bytes], str]]] = []
256
+ for path in file_paths:
257
+ # Detect MIME (content type)
258
+ mime = _detect_mime(path)
259
+
260
+ # Open file and register with ExitStack
261
+ # pylint: disable-next=consider-using-with
262
+ fobj = stack.enter_context(open(path.resolve(), "rb"))
263
+ typer.echo(f"Attach {path} ({mime}, {path.stat().st_size:,} bytes)")
264
+
265
+ # Get relative POSIX path
266
+ relative_posix = path.relative_to(root).as_posix()
267
+
268
+ # Append to form data (key, (filename, fileobj, mime))
269
+ form.append(("files", (relative_posix, fobj, mime)))
270
+ return form
271
+
272
+
273
+ def _post_files(
274
+ files_param: list[tuple[str, tuple[str, IO[bytes], str]]],
275
+ token: str,
276
+ ) -> Response:
277
+ """POST multipart with one part per file."""
278
+ url = f"{PLATFORM_API_URL}/hub/apps/publish"
279
+ headers = {"Authorization": f"Bearer {token}"}
280
+ body = {"flwr_version": flwr_version}
281
+
282
+ resp = requests.post(
283
+ url, files=files_param, headers=headers, json=body, timeout=120
284
+ )
285
+ return resp