flwr 1.22.0__py3-none-any.whl → 1.24.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 (301) 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 +34 -1
  5. flwr/cli/app_cmd/__init__.py +23 -0
  6. flwr/cli/app_cmd/publish.py +285 -0
  7. flwr/cli/app_cmd/review.py +252 -0
  8. flwr/cli/auth_plugin/__init__.py +15 -6
  9. flwr/cli/auth_plugin/auth_plugin.py +94 -0
  10. flwr/cli/auth_plugin/noop_auth_plugin.py +101 -0
  11. flwr/cli/auth_plugin/oidc_cli_plugin.py +46 -32
  12. flwr/cli/build.py +166 -53
  13. flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +29 -11
  14. flwr/cli/config_utils.py +101 -13
  15. flwr/cli/federation/__init__.py +24 -0
  16. flwr/cli/federation/ls.py +140 -0
  17. flwr/cli/federation/show.py +317 -0
  18. flwr/cli/install.py +91 -13
  19. flwr/cli/log.py +54 -11
  20. flwr/cli/login/login.py +41 -27
  21. flwr/cli/ls.py +177 -133
  22. flwr/cli/new/new.py +175 -40
  23. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +1 -0
  24. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +1 -1
  25. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +1 -1
  26. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  27. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  28. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  29. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  30. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
  31. flwr/cli/new/templates/app/pyproject.pytorch_legacy_api.toml.tpl +1 -1
  32. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  33. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +2 -2
  34. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +1 -1
  35. flwr/cli/pull.py +12 -7
  36. flwr/cli/run/run.py +82 -31
  37. flwr/cli/run_utils.py +130 -0
  38. flwr/cli/stop.py +27 -9
  39. flwr/cli/supernode/__init__.py +25 -0
  40. flwr/cli/supernode/ls.py +268 -0
  41. flwr/cli/supernode/register.py +190 -0
  42. flwr/cli/supernode/unregister.py +140 -0
  43. flwr/cli/utils.py +464 -81
  44. flwr/client/__init__.py +2 -1
  45. flwr/client/dpfedavg_numpy_client.py +4 -1
  46. flwr/client/grpc_adapter_client/connection.py +12 -15
  47. flwr/client/grpc_rere_client/connection.py +68 -41
  48. flwr/client/grpc_rere_client/grpc_adapter.py +34 -14
  49. flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +5 -7
  50. flwr/client/message_handler/message_handler.py +2 -2
  51. flwr/client/mod/secure_aggregation/secaggplus_mod.py +10 -8
  52. flwr/client/numpy_client.py +1 -1
  53. flwr/client/rest_client/connection.py +94 -51
  54. flwr/client/run_info_store.py +4 -5
  55. flwr/client/typing.py +1 -1
  56. flwr/clientapp/__init__.py +1 -2
  57. flwr/{client → clientapp}/client_app.py +9 -10
  58. flwr/clientapp/mod/centraldp_mods.py +16 -17
  59. flwr/clientapp/mod/localdp_mod.py +8 -9
  60. flwr/clientapp/typing.py +1 -1
  61. flwr/{client/clientapp → clientapp}/utils.py +4 -4
  62. flwr/common/address.py +1 -2
  63. flwr/common/args.py +3 -4
  64. flwr/common/config.py +13 -16
  65. flwr/common/constant.py +56 -13
  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 +15 -2
  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 +6 -6
  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 +4 -4
  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 +3 -3
  87. flwr/common/serde.py +9 -6
  88. flwr/common/serde_utils.py +2 -2
  89. flwr/common/telemetry.py +9 -5
  90. flwr/common/typing.py +59 -43
  91. flwr/compat/client/app.py +39 -38
  92. flwr/compat/client/grpc_client/connection.py +13 -13
  93. flwr/compat/server/app.py +5 -6
  94. flwr/proto/appio_pb2.py +13 -3
  95. flwr/proto/appio_pb2.pyi +134 -65
  96. flwr/proto/appio_pb2_grpc.py +20 -0
  97. flwr/proto/appio_pb2_grpc.pyi +27 -0
  98. flwr/proto/clientappio_pb2.py +17 -7
  99. flwr/proto/clientappio_pb2.pyi +15 -0
  100. flwr/proto/clientappio_pb2_grpc.py +206 -40
  101. flwr/proto/clientappio_pb2_grpc.pyi +168 -53
  102. flwr/proto/control_pb2.py +72 -40
  103. flwr/proto/control_pb2.pyi +319 -87
  104. flwr/proto/control_pb2_grpc.py +339 -28
  105. flwr/proto/control_pb2_grpc.pyi +209 -37
  106. flwr/proto/error_pb2.py +13 -3
  107. flwr/proto/error_pb2.pyi +24 -6
  108. flwr/proto/error_pb2_grpc.py +20 -0
  109. flwr/proto/error_pb2_grpc.pyi +27 -0
  110. flwr/proto/fab_pb2.py +24 -10
  111. flwr/proto/fab_pb2.pyi +68 -20
  112. flwr/proto/fab_pb2_grpc.py +20 -0
  113. flwr/proto/fab_pb2_grpc.pyi +27 -0
  114. flwr/proto/federation_pb2.py +38 -0
  115. flwr/proto/federation_pb2.pyi +56 -0
  116. flwr/proto/federation_pb2_grpc.py +24 -0
  117. flwr/proto/federation_pb2_grpc.pyi +31 -0
  118. flwr/proto/fleet_pb2.py +45 -27
  119. flwr/proto/fleet_pb2.pyi +186 -70
  120. flwr/proto/fleet_pb2_grpc.py +277 -66
  121. flwr/proto/fleet_pb2_grpc.pyi +201 -55
  122. flwr/proto/grpcadapter_pb2.py +14 -4
  123. flwr/proto/grpcadapter_pb2.pyi +38 -16
  124. flwr/proto/grpcadapter_pb2_grpc.py +35 -4
  125. flwr/proto/grpcadapter_pb2_grpc.pyi +38 -7
  126. flwr/proto/heartbeat_pb2.py +17 -7
  127. flwr/proto/heartbeat_pb2.pyi +51 -22
  128. flwr/proto/heartbeat_pb2_grpc.py +20 -0
  129. flwr/proto/heartbeat_pb2_grpc.pyi +27 -0
  130. flwr/proto/log_pb2.py +13 -3
  131. flwr/proto/log_pb2.pyi +34 -11
  132. flwr/proto/log_pb2_grpc.py +20 -0
  133. flwr/proto/log_pb2_grpc.pyi +27 -0
  134. flwr/proto/message_pb2.py +15 -5
  135. flwr/proto/message_pb2.pyi +154 -86
  136. flwr/proto/message_pb2_grpc.py +20 -0
  137. flwr/proto/message_pb2_grpc.pyi +27 -0
  138. flwr/proto/node_pb2.py +16 -4
  139. flwr/proto/node_pb2.pyi +77 -4
  140. flwr/proto/node_pb2_grpc.py +20 -0
  141. flwr/proto/node_pb2_grpc.pyi +27 -0
  142. flwr/proto/recorddict_pb2.py +13 -3
  143. flwr/proto/recorddict_pb2.pyi +184 -107
  144. flwr/proto/recorddict_pb2_grpc.py +20 -0
  145. flwr/proto/recorddict_pb2_grpc.pyi +27 -0
  146. flwr/proto/run_pb2.py +40 -31
  147. flwr/proto/run_pb2.pyi +149 -84
  148. flwr/proto/run_pb2_grpc.py +20 -0
  149. flwr/proto/run_pb2_grpc.pyi +27 -0
  150. flwr/proto/serverappio_pb2.py +13 -3
  151. flwr/proto/serverappio_pb2.pyi +32 -8
  152. flwr/proto/serverappio_pb2_grpc.py +246 -65
  153. flwr/proto/serverappio_pb2_grpc.pyi +221 -85
  154. flwr/proto/simulationio_pb2.py +16 -8
  155. flwr/proto/simulationio_pb2.pyi +15 -0
  156. flwr/proto/simulationio_pb2_grpc.py +162 -41
  157. flwr/proto/simulationio_pb2_grpc.pyi +149 -55
  158. flwr/proto/transport_pb2.py +20 -10
  159. flwr/proto/transport_pb2.pyi +249 -160
  160. flwr/proto/transport_pb2_grpc.py +35 -4
  161. flwr/proto/transport_pb2_grpc.pyi +38 -8
  162. flwr/server/app.py +173 -127
  163. flwr/server/client_manager.py +4 -5
  164. flwr/server/client_proxy.py +10 -11
  165. flwr/server/compat/app.py +4 -5
  166. flwr/server/compat/app_utils.py +2 -1
  167. flwr/server/compat/grid_client_proxy.py +10 -12
  168. flwr/server/compat/legacy_context.py +3 -4
  169. flwr/server/fleet_event_log_interceptor.py +2 -1
  170. flwr/server/grid/grid.py +2 -3
  171. flwr/server/grid/grpc_grid.py +10 -8
  172. flwr/server/grid/inmemory_grid.py +4 -4
  173. flwr/server/run_serverapp.py +2 -3
  174. flwr/server/server.py +34 -39
  175. flwr/server/server_app.py +7 -8
  176. flwr/server/server_config.py +1 -2
  177. flwr/server/serverapp/app.py +34 -28
  178. flwr/server/serverapp_components.py +4 -5
  179. flwr/server/strategy/aggregate.py +9 -8
  180. flwr/server/strategy/bulyan.py +13 -11
  181. flwr/server/strategy/dp_adaptive_clipping.py +16 -20
  182. flwr/server/strategy/dp_fixed_clipping.py +12 -17
  183. flwr/server/strategy/dpfedavg_adaptive.py +3 -4
  184. flwr/server/strategy/dpfedavg_fixed.py +6 -10
  185. flwr/server/strategy/fault_tolerant_fedavg.py +14 -13
  186. flwr/server/strategy/fedadagrad.py +18 -14
  187. flwr/server/strategy/fedadam.py +16 -14
  188. flwr/server/strategy/fedavg.py +16 -17
  189. flwr/server/strategy/fedavg_android.py +15 -15
  190. flwr/server/strategy/fedavgm.py +21 -18
  191. flwr/server/strategy/fedmedian.py +2 -3
  192. flwr/server/strategy/fedopt.py +11 -10
  193. flwr/server/strategy/fedprox.py +10 -9
  194. flwr/server/strategy/fedtrimmedavg.py +12 -11
  195. flwr/server/strategy/fedxgb_bagging.py +13 -11
  196. flwr/server/strategy/fedxgb_cyclic.py +6 -6
  197. flwr/server/strategy/fedxgb_nn_avg.py +4 -4
  198. flwr/server/strategy/fedyogi.py +16 -14
  199. flwr/server/strategy/krum.py +12 -11
  200. flwr/server/strategy/qfedavg.py +16 -15
  201. flwr/server/strategy/strategy.py +6 -9
  202. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +19 -8
  203. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -2
  204. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -4
  205. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +10 -12
  206. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +1 -3
  207. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +136 -42
  208. flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +28 -51
  209. flwr/server/superlink/fleet/message_handler/message_handler.py +100 -49
  210. flwr/server/superlink/fleet/rest_rere/rest_api.py +54 -33
  211. flwr/server/superlink/fleet/vce/backend/backend.py +2 -2
  212. flwr/server/superlink/fleet/vce/backend/raybackend.py +6 -6
  213. flwr/server/superlink/fleet/vce/vce_api.py +32 -13
  214. flwr/server/superlink/linkstate/in_memory_linkstate.py +266 -207
  215. flwr/server/superlink/linkstate/linkstate.py +161 -62
  216. flwr/server/superlink/linkstate/linkstate_factory.py +24 -6
  217. flwr/server/superlink/linkstate/sqlite_linkstate.py +698 -638
  218. flwr/server/superlink/linkstate/utils.py +9 -60
  219. flwr/server/superlink/serverappio/serverappio_grpc.py +1 -2
  220. flwr/server/superlink/serverappio/serverappio_servicer.py +28 -23
  221. flwr/server/superlink/simulation/simulationio_grpc.py +1 -2
  222. flwr/server/superlink/simulation/simulationio_servicer.py +19 -14
  223. flwr/server/superlink/utils.py +4 -6
  224. flwr/server/typing.py +1 -1
  225. flwr/server/utils/tensorboard.py +15 -8
  226. flwr/server/utils/validator.py +2 -3
  227. flwr/server/workflow/default_workflows.py +5 -5
  228. flwr/server/workflow/secure_aggregation/secagg_workflow.py +2 -4
  229. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +12 -10
  230. flwr/serverapp/strategy/bulyan.py +16 -15
  231. flwr/serverapp/strategy/dp_adaptive_clipping.py +12 -11
  232. flwr/serverapp/strategy/dp_fixed_clipping.py +11 -14
  233. flwr/serverapp/strategy/fedadagrad.py +10 -11
  234. flwr/serverapp/strategy/fedadam.py +10 -11
  235. flwr/serverapp/strategy/fedavg.py +9 -10
  236. flwr/serverapp/strategy/fedavgm.py +17 -16
  237. flwr/serverapp/strategy/fedmedian.py +2 -2
  238. flwr/serverapp/strategy/fedopt.py +10 -11
  239. flwr/serverapp/strategy/fedprox.py +7 -8
  240. flwr/serverapp/strategy/fedtrimmedavg.py +9 -9
  241. flwr/serverapp/strategy/fedxgb_bagging.py +3 -3
  242. flwr/serverapp/strategy/fedxgb_cyclic.py +9 -9
  243. flwr/serverapp/strategy/fedyogi.py +9 -11
  244. flwr/serverapp/strategy/krum.py +7 -7
  245. flwr/serverapp/strategy/multikrum.py +9 -9
  246. flwr/serverapp/strategy/qfedavg.py +17 -16
  247. flwr/serverapp/strategy/strategy.py +6 -9
  248. flwr/serverapp/strategy/strategy_utils.py +7 -8
  249. flwr/simulation/app.py +46 -42
  250. flwr/simulation/legacy_app.py +12 -12
  251. flwr/simulation/ray_transport/ray_actor.py +11 -12
  252. flwr/simulation/ray_transport/ray_client_proxy.py +12 -13
  253. flwr/simulation/run_simulation.py +44 -43
  254. flwr/simulation/simulationio_connection.py +4 -4
  255. flwr/supercore/cli/flower_superexec.py +3 -4
  256. flwr/supercore/constant.py +52 -0
  257. flwr/supercore/corestate/corestate.py +24 -3
  258. flwr/supercore/corestate/in_memory_corestate.py +138 -0
  259. flwr/supercore/corestate/sqlite_corestate.py +157 -0
  260. flwr/supercore/ffs/disk_ffs.py +1 -2
  261. flwr/supercore/ffs/ffs.py +1 -2
  262. flwr/supercore/ffs/ffs_factory.py +1 -2
  263. flwr/{common → supercore}/heartbeat.py +20 -25
  264. flwr/supercore/object_store/in_memory_object_store.py +1 -6
  265. flwr/supercore/object_store/object_store.py +1 -2
  266. flwr/supercore/object_store/object_store_factory.py +27 -8
  267. flwr/supercore/object_store/sqlite_object_store.py +253 -0
  268. flwr/{client/clientapp → supercore/primitives}/__init__.py +1 -1
  269. flwr/supercore/primitives/asymmetric.py +117 -0
  270. flwr/supercore/primitives/asymmetric_ed25519.py +175 -0
  271. flwr/supercore/sqlite_mixin.py +159 -0
  272. flwr/supercore/superexec/plugin/base_exec_plugin.py +1 -2
  273. flwr/supercore/superexec/plugin/exec_plugin.py +3 -3
  274. flwr/supercore/superexec/run_superexec.py +9 -13
  275. flwr/supercore/utils.py +20 -0
  276. flwr/superlink/artifact_provider/artifact_provider.py +1 -2
  277. flwr/{common → superlink}/auth_plugin/__init__.py +6 -6
  278. flwr/superlink/auth_plugin/auth_plugin.py +88 -0
  279. flwr/superlink/auth_plugin/noop_auth_plugin.py +84 -0
  280. flwr/superlink/federation/__init__.py +24 -0
  281. flwr/superlink/federation/federation_manager.py +64 -0
  282. flwr/superlink/federation/noop_federation_manager.py +71 -0
  283. flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +41 -32
  284. flwr/superlink/servicer/control/control_event_log_interceptor.py +7 -7
  285. flwr/superlink/servicer/control/control_grpc.py +18 -17
  286. flwr/superlink/servicer/control/control_license_interceptor.py +3 -3
  287. flwr/superlink/servicer/control/control_servicer.py +239 -63
  288. flwr/supernode/cli/flower_supernode.py +74 -26
  289. flwr/supernode/nodestate/in_memory_nodestate.py +60 -49
  290. flwr/supernode/nodestate/nodestate.py +7 -8
  291. flwr/supernode/nodestate/nodestate_factory.py +7 -4
  292. flwr/supernode/runtime/run_clientapp.py +43 -24
  293. flwr/supernode/servicer/clientappio/clientappio_servicer.py +40 -10
  294. flwr/supernode/start_client_internal.py +175 -51
  295. {flwr-1.22.0.dist-info → flwr-1.24.0.dist-info}/METADATA +8 -8
  296. flwr-1.24.0.dist-info/RECORD +454 -0
  297. flwr/common/auth_plugin/auth_plugin.py +0 -149
  298. flwr/supercore/object_store/utils.py +0 -43
  299. flwr-1.22.0.dist-info/RECORD +0 -428
  300. {flwr-1.22.0.dist-info → flwr-1.24.0.dist-info}/WHEEL +0 -0
  301. {flwr-1.22.0.dist-info → flwr-1.24.0.dist-info}/entry_points.txt +0 -0
@@ -16,19 +16,26 @@
16
16
 
17
17
 
18
18
  import abc
19
- from typing import Optional
19
+ from collections.abc import Sequence
20
20
 
21
21
  from flwr.common import Context, Message
22
22
  from flwr.common.record import ConfigRecord
23
23
  from flwr.common.typing import Run, RunStatus, UserConfig
24
+ from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
24
25
  from flwr.supercore.corestate import CoreState
26
+ from flwr.superlink.federation import FederationManager
25
27
 
26
28
 
27
29
  class LinkState(CoreState): # pylint: disable=R0904
28
30
  """Abstract LinkState."""
29
31
 
32
+ @property
30
33
  @abc.abstractmethod
31
- def store_message_ins(self, message: Message) -> Optional[str]:
34
+ def federation_manager(self) -> FederationManager:
35
+ """Return the FederationManager instance."""
36
+
37
+ @abc.abstractmethod
38
+ def store_message_ins(self, message: Message) -> str | None:
32
39
  """Store one Message.
33
40
 
34
41
  Usually, the ServerAppIo API calls this to schedule instructions.
@@ -46,7 +53,7 @@ class LinkState(CoreState): # pylint: disable=R0904
46
53
  """
47
54
 
48
55
  @abc.abstractmethod
49
- def get_message_ins(self, node_id: int, limit: Optional[int]) -> list[Message]:
56
+ def get_message_ins(self, node_id: int, limit: int | None) -> list[Message]:
50
57
  """Get zero or more `Message` objects for the provided `node_id`.
51
58
 
52
59
  Usually, the Fleet API calls this for Nodes planning to work on one or more
@@ -61,7 +68,7 @@ class LinkState(CoreState): # pylint: disable=R0904
61
68
  """
62
69
 
63
70
  @abc.abstractmethod
64
- def store_message_res(self, message: Message) -> Optional[str]:
71
+ def store_message_res(self, message: Message) -> str | None:
65
72
  """Store one Message.
66
73
 
67
74
  Usually, the Fleet API calls this for Nodes returning results.
@@ -128,13 +135,58 @@ class LinkState(CoreState): # pylint: disable=R0904
128
135
  """Get all instruction Message IDs for the given run_id."""
129
136
 
130
137
  @abc.abstractmethod
131
- def create_node(self, heartbeat_interval: float) -> int:
138
+ def create_node(
139
+ self,
140
+ owner_aid: str,
141
+ owner_name: str,
142
+ public_key: bytes,
143
+ heartbeat_interval: float,
144
+ ) -> int:
132
145
  """Create, store in the link state, and return `node_id`."""
133
146
 
134
147
  @abc.abstractmethod
135
- def delete_node(self, node_id: int) -> None:
148
+ def delete_node(self, owner_aid: str, node_id: int) -> None:
136
149
  """Remove `node_id` from the link state."""
137
150
 
151
+ @abc.abstractmethod
152
+ def activate_node(self, node_id: int, heartbeat_interval: float) -> bool:
153
+ """Activate the node with the specified `node_id`.
154
+
155
+ Transitions the node status to "online". The transition will fail
156
+ if the current status is not "registered" or "offline".
157
+
158
+ Parameters
159
+ ----------
160
+ node_id : int
161
+ The identifier of the node to activate.
162
+ heartbeat_interval : float
163
+ The interval (in seconds) from the current timestamp within which
164
+ the next heartbeat from this node is expected to be received.
165
+
166
+ Returns
167
+ -------
168
+ bool
169
+ True if the status transition was successful, False otherwise.
170
+ """
171
+
172
+ @abc.abstractmethod
173
+ def deactivate_node(self, node_id: int) -> bool:
174
+ """Deactivate the node with the specified `node_id`.
175
+
176
+ Transitions the node status to "offline". The transition will fail
177
+ if the current status is not "online".
178
+
179
+ Parameters
180
+ ----------
181
+ node_id : int
182
+ The identifier of the node to deactivate.
183
+
184
+ Returns
185
+ -------
186
+ bool
187
+ True if the status transition was successful, False otherwise.
188
+ """
189
+
138
190
  @abc.abstractmethod
139
191
  def get_nodes(self, run_id: int) -> set[int]:
140
192
  """Retrieve all currently stored node IDs as a set.
@@ -146,38 +198,124 @@ class LinkState(CoreState): # pylint: disable=R0904
146
198
  """
147
199
 
148
200
  @abc.abstractmethod
149
- def set_node_public_key(self, node_id: int, public_key: bytes) -> None:
150
- """Set `public_key` for the specified `node_id`."""
201
+ def get_node_id_by_public_key(self, public_key: bytes) -> int | None:
202
+ """Get `node_id` for the specified `public_key` if it exists and is not deleted.
203
+
204
+ Parameters
205
+ ----------
206
+ public_key : bytes
207
+ The public key of the node whose information is to be retrieved.
208
+
209
+ Returns
210
+ -------
211
+ Optional[int]
212
+ The `node_id` associated with the specified `public_key` if it exists
213
+ and is not deleted; otherwise, `None`.
214
+ """
151
215
 
152
216
  @abc.abstractmethod
153
- def get_node_public_key(self, node_id: int) -> Optional[bytes]:
154
- """Get `public_key` for the specified `node_id`."""
217
+ def get_node_info(
218
+ self,
219
+ *,
220
+ node_ids: Sequence[int] | None = None,
221
+ owner_aids: Sequence[str] | None = None,
222
+ statuses: Sequence[str] | None = None,
223
+ ) -> Sequence[NodeInfo]:
224
+ """Retrieve information about nodes based on the specified filters.
225
+
226
+ If a filter is set to None, it is ignored.
227
+ If multiple filters are provided, they are combined using AND logic.
228
+
229
+ Parameters
230
+ ----------
231
+ node_ids : Optional[Sequence[int]] (default: None)
232
+ Sequence of node IDs to filter by. If a sequence is provided,
233
+ it is treated as an OR condition.
234
+ owner_aids : Optional[Sequence[str]] (default: None)
235
+ Sequence of owner account IDs to filter by. If a sequence is provided,
236
+ it is treated as an OR condition.
237
+ statuses : Optional[Sequence[str]] (default: None)
238
+ Sequence of node status values (e.g., "created", "activated")
239
+ to filter by. If a sequence is provided, it is treated as an OR condition.
240
+
241
+ Returns
242
+ -------
243
+ Sequence[NodeInfo]
244
+ A sequence of NodeInfo objects representing the nodes matching
245
+ the specified filters.
246
+ """
155
247
 
156
248
  @abc.abstractmethod
157
- def get_node_id(self, node_public_key: bytes) -> Optional[int]:
158
- """Retrieve stored `node_id` filtered by `node_public_keys`."""
249
+ def get_node_public_key(self, node_id: int) -> bytes:
250
+ """Get `public_key` for the specified `node_id`.
251
+
252
+ Parameters
253
+ ----------
254
+ node_id : int
255
+ The identifier of the node whose public key is to be retrieved.
256
+
257
+ Returns
258
+ -------
259
+ bytes
260
+ The public key associated with the specified `node_id`.
261
+
262
+ Raises
263
+ ------
264
+ ValueError
265
+ If the specified `node_id` does not exist in the link state.
266
+ """
159
267
 
160
268
  @abc.abstractmethod
161
269
  def create_run( # pylint: disable=too-many-arguments,too-many-positional-arguments
162
270
  self,
163
- fab_id: Optional[str],
164
- fab_version: Optional[str],
165
- fab_hash: Optional[str],
271
+ fab_id: str | None,
272
+ fab_version: str | None,
273
+ fab_hash: str | None,
166
274
  override_config: UserConfig,
275
+ federation: str,
167
276
  federation_options: ConfigRecord,
168
- flwr_aid: Optional[str],
277
+ flwr_aid: str | None,
169
278
  ) -> int:
170
- """Create a new run for the specified `fab_hash`."""
279
+ """Create a new run.
280
+
281
+ Parameters
282
+ ----------
283
+ fab_id : Optional[str]
284
+ The ID of the FAB, of format `<publisher>/<app-name>`.
285
+ fab_version : Optional[str]
286
+ The version of the FAB.
287
+ fab_hash : Optional[str]
288
+ The SHA256 hex hash of the FAB.
289
+ override_config : UserConfig
290
+ Configuration overrides for the run config.
291
+ federation : str
292
+ The federation this run belongs to.
293
+ federation_options : ConfigRecord
294
+ Federation configurations. For now, only `num-supernodes` for
295
+ the simulation runtime.
296
+ flwr_aid : Optional[str]
297
+ Flower Account ID of the creator.
298
+
299
+ Returns
300
+ -------
301
+ int
302
+ The run ID of the newly created run.
303
+
304
+ Notes
305
+ -----
306
+ This method will not verify if the account has permission to create
307
+ a run in the federation.
308
+ """
171
309
 
172
310
  @abc.abstractmethod
173
- def get_run_ids(self, flwr_aid: Optional[str]) -> set[int]:
311
+ def get_run_ids(self, flwr_aid: str | None) -> set[int]:
174
312
  """Retrieve all run IDs if `flwr_aid` is not specified.
175
313
 
176
314
  Otherwise, retrieve all run IDs for the specified `flwr_aid`.
177
315
  """
178
316
 
179
317
  @abc.abstractmethod
180
- def get_run(self, run_id: int) -> Optional[Run]:
318
+ def get_run(self, run_id: int) -> Run | None:
181
319
  """Retrieve information about the run with the specified `run_id`.
182
320
 
183
321
  Parameters
@@ -229,7 +367,7 @@ class LinkState(CoreState): # pylint: disable=R0904
229
367
  """
230
368
 
231
369
  @abc.abstractmethod
232
- def get_pending_run_id(self) -> Optional[int]:
370
+ def get_pending_run_id(self) -> int | None:
233
371
  """Get the `run_id` of a run with `Status.PENDING` status.
234
372
 
235
373
  Returns
@@ -240,7 +378,7 @@ class LinkState(CoreState): # pylint: disable=R0904
240
378
  """
241
379
 
242
380
  @abc.abstractmethod
243
- def get_federation_options(self, run_id: int) -> Optional[ConfigRecord]:
381
+ def get_federation_options(self, run_id: int) -> ConfigRecord | None:
244
382
  """Retrieve the federation options for the specified `run_id`.
245
383
 
246
384
  Parameters
@@ -254,22 +392,6 @@ class LinkState(CoreState): # pylint: disable=R0904
254
392
  The federation options for the run if it exists; None otherwise.
255
393
  """
256
394
 
257
- @abc.abstractmethod
258
- def clear_supernode_auth_keys(self) -> None:
259
- """Clear stored `node_public_keys` in the link state if any."""
260
-
261
- @abc.abstractmethod
262
- def store_node_public_keys(self, public_keys: set[bytes]) -> None:
263
- """Store a set of `node_public_keys` in the link state."""
264
-
265
- @abc.abstractmethod
266
- def store_node_public_key(self, public_key: bytes) -> None:
267
- """Store a `node_public_key` in the link state."""
268
-
269
- @abc.abstractmethod
270
- def get_node_public_keys(self) -> set[bytes]:
271
- """Retrieve all currently stored `node_public_keys` as a set."""
272
-
273
395
  @abc.abstractmethod
274
396
  def acknowledge_node_heartbeat(
275
397
  self, node_id: int, heartbeat_interval: float
@@ -297,30 +419,7 @@ class LinkState(CoreState): # pylint: disable=R0904
297
419
  """
298
420
 
299
421
  @abc.abstractmethod
300
- def acknowledge_app_heartbeat(self, run_id: int, heartbeat_interval: float) -> bool:
301
- """Acknowledge a heartbeat received from a ServerApp for a given run.
302
-
303
- A run with status `"running"` is considered alive as long as it sends heartbeats
304
- within the tolerated interval: HEARTBEAT_PATIENCE × heartbeat_interval.
305
- HEARTBEAT_PATIENCE = N allows for N-1 missed heartbeat before the run is
306
- marked as `"completed:failed"`.
307
-
308
- Parameters
309
- ----------
310
- run_id : int
311
- The `run_id` from which the heartbeat was received.
312
- heartbeat_interval : float
313
- The interval (in seconds) from the current timestamp within which the next
314
- heartbeat from the ServerApp for this run must be received.
315
-
316
- Returns
317
- -------
318
- is_acknowledged : bool
319
- True if the heartbeat is successfully acknowledged; otherwise, False.
320
- """
321
-
322
- @abc.abstractmethod
323
- def get_serverapp_context(self, run_id: int) -> Optional[Context]:
422
+ def get_serverapp_context(self, run_id: int) -> Context | None:
324
423
  """Get the context for the specified `run_id`.
325
424
 
326
425
  Parameters
@@ -361,7 +460,7 @@ class LinkState(CoreState): # pylint: disable=R0904
361
460
 
362
461
  @abc.abstractmethod
363
462
  def get_serverapp_log(
364
- self, run_id: int, after_timestamp: Optional[float]
463
+ self, run_id: int, after_timestamp: float | None
365
464
  ) -> tuple[str, float]:
366
465
  """Get the ServerApp logs for the specified `run_id`.
367
466
 
@@ -16,9 +16,11 @@
16
16
 
17
17
 
18
18
  from logging import DEBUG
19
- from typing import Optional
20
19
 
21
20
  from flwr.common.logger import log
21
+ from flwr.supercore.constant import FLWR_IN_MEMORY_DB_NAME
22
+ from flwr.supercore.object_store import ObjectStoreFactory
23
+ from flwr.superlink.federation import FederationManager
22
24
 
23
25
  from .in_memory_linkstate import InMemoryLinkState
24
26
  from .linkstate import LinkState
@@ -35,23 +37,39 @@ class LinkStateFactory:
35
37
  Note that passing ':memory:' will open a connection to a database that is
36
38
  in RAM, instead of on disk. For more information on special in-memory
37
39
  databases, please refer to https://sqlite.org/inmemorydb.html.
40
+ federation_manager : FederationManager
41
+ An instance of FederationManager to manage federations.
42
+ objectstore_factory : ObjectStoreFactory
43
+ An instance of ObjectStoreFactory to create object stores.
38
44
  """
39
45
 
40
- def __init__(self, database: str) -> None:
46
+ def __init__(
47
+ self,
48
+ database: str,
49
+ federation_manager: FederationManager,
50
+ objectstore_factory: ObjectStoreFactory,
51
+ ) -> None:
41
52
  self.database = database
42
- self.state_instance: Optional[LinkState] = None
53
+ self.state_instance: LinkState | None = None
54
+ self.federation_manager = federation_manager
55
+ self.objectstore_factory = objectstore_factory
43
56
 
44
57
  def state(self) -> LinkState:
45
58
  """Return a State instance and create it, if necessary."""
59
+ # Get the ObjectStore instance
60
+ object_store = self.objectstore_factory.store()
61
+
46
62
  # InMemoryState
47
- if self.database == ":flwr-in-memory-state:":
63
+ if self.database == FLWR_IN_MEMORY_DB_NAME:
48
64
  if self.state_instance is None:
49
- self.state_instance = InMemoryLinkState()
65
+ self.state_instance = InMemoryLinkState(
66
+ self.federation_manager, object_store
67
+ )
50
68
  log(DEBUG, "Using InMemoryState")
51
69
  return self.state_instance
52
70
 
53
71
  # SqliteState
54
- state = SqliteLinkState(self.database)
72
+ state = SqliteLinkState(self.database, self.federation_manager, object_store)
55
73
  state.initialize()
56
74
  log(DEBUG, "Using SqliteState")
57
75
  return state