flwr-nightly 1.8.0.dev20240315__py3-none-any.whl → 1.11.0.dev20240813__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.

Potentially problematic release.


This version of flwr-nightly might be problematic. Click here for more details.

Files changed (237) hide show
  1. flwr/cli/app.py +7 -0
  2. flwr/cli/build.py +150 -0
  3. flwr/cli/config_utils.py +219 -0
  4. flwr/cli/example.py +3 -1
  5. flwr/cli/install.py +227 -0
  6. flwr/cli/new/new.py +179 -48
  7. flwr/cli/new/templates/app/.gitignore.tpl +160 -0
  8. flwr/cli/new/templates/app/README.flowertune.md.tpl +56 -0
  9. flwr/cli/new/templates/app/README.md.tpl +1 -5
  10. flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
  11. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +65 -0
  12. flwr/cli/new/templates/app/code/client.jax.py.tpl +56 -0
  13. flwr/cli/new/templates/app/code/client.mlx.py.tpl +93 -0
  14. flwr/cli/new/templates/app/code/client.numpy.py.tpl +3 -2
  15. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +23 -11
  16. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +97 -0
  17. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +60 -1
  18. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
  19. flwr/cli/new/templates/app/code/flwr_tune/app.py.tpl +89 -0
  20. flwr/cli/new/templates/app/code/flwr_tune/client.py.tpl +126 -0
  21. flwr/cli/new/templates/app/code/flwr_tune/config.yaml.tpl +34 -0
  22. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +57 -0
  23. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +59 -0
  24. flwr/cli/new/templates/app/code/flwr_tune/server.py.tpl +48 -0
  25. flwr/cli/new/templates/app/code/flwr_tune/static_config.yaml.tpl +11 -0
  26. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -0
  27. flwr/cli/new/templates/app/code/server.jax.py.tpl +20 -0
  28. flwr/cli/new/templates/app/code/server.mlx.py.tpl +20 -0
  29. flwr/cli/new/templates/app/code/server.numpy.py.tpl +17 -9
  30. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
  31. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +24 -0
  32. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
  33. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +99 -0
  34. flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
  35. flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
  36. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +28 -23
  37. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
  38. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +39 -0
  39. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +38 -0
  40. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +34 -0
  41. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
  42. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
  43. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
  44. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +33 -0
  45. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
  46. flwr/cli/run/run.py +168 -17
  47. flwr/cli/utils.py +75 -4
  48. flwr/client/__init__.py +6 -1
  49. flwr/client/app.py +239 -248
  50. flwr/client/client_app.py +70 -9
  51. flwr/client/dpfedavg_numpy_client.py +1 -1
  52. flwr/client/grpc_adapter_client/__init__.py +15 -0
  53. flwr/client/grpc_adapter_client/connection.py +97 -0
  54. flwr/client/grpc_client/connection.py +18 -5
  55. flwr/client/grpc_rere_client/__init__.py +1 -1
  56. flwr/client/grpc_rere_client/client_interceptor.py +158 -0
  57. flwr/client/grpc_rere_client/connection.py +127 -33
  58. flwr/client/grpc_rere_client/grpc_adapter.py +140 -0
  59. flwr/client/heartbeat.py +74 -0
  60. flwr/client/message_handler/__init__.py +1 -1
  61. flwr/client/message_handler/message_handler.py +7 -7
  62. flwr/client/mod/__init__.py +5 -5
  63. flwr/client/mod/centraldp_mods.py +4 -2
  64. flwr/client/mod/comms_mods.py +4 -4
  65. flwr/client/mod/localdp_mod.py +9 -4
  66. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  67. flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
  68. flwr/client/mod/utils.py +1 -1
  69. flwr/client/node_state.py +60 -10
  70. flwr/client/node_state_tests.py +4 -3
  71. flwr/client/rest_client/__init__.py +1 -1
  72. flwr/client/rest_client/connection.py +177 -157
  73. flwr/client/supernode/__init__.py +26 -0
  74. flwr/client/supernode/app.py +464 -0
  75. flwr/client/typing.py +1 -0
  76. flwr/common/__init__.py +13 -11
  77. flwr/common/address.py +1 -1
  78. flwr/common/config.py +193 -0
  79. flwr/common/constant.py +42 -1
  80. flwr/common/context.py +26 -1
  81. flwr/common/date.py +1 -1
  82. flwr/common/dp.py +1 -1
  83. flwr/common/grpc.py +6 -2
  84. flwr/common/logger.py +79 -8
  85. flwr/common/message.py +167 -105
  86. flwr/common/object_ref.py +126 -25
  87. flwr/common/record/__init__.py +1 -1
  88. flwr/common/record/parametersrecord.py +0 -1
  89. flwr/common/record/recordset.py +78 -27
  90. flwr/common/recordset_compat.py +8 -1
  91. flwr/common/retry_invoker.py +25 -13
  92. flwr/common/secure_aggregation/__init__.py +1 -1
  93. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  94. flwr/common/secure_aggregation/crypto/shamir.py +1 -1
  95. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +21 -2
  96. flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
  97. flwr/common/secure_aggregation/quantization.py +1 -1
  98. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  99. flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
  100. flwr/common/serde.py +209 -3
  101. flwr/common/telemetry.py +25 -0
  102. flwr/common/typing.py +38 -0
  103. flwr/common/version.py +14 -0
  104. flwr/proto/clientappio_pb2.py +41 -0
  105. flwr/proto/clientappio_pb2.pyi +110 -0
  106. flwr/proto/clientappio_pb2_grpc.py +101 -0
  107. flwr/proto/clientappio_pb2_grpc.pyi +40 -0
  108. flwr/proto/common_pb2.py +36 -0
  109. flwr/proto/common_pb2.pyi +121 -0
  110. flwr/proto/common_pb2_grpc.py +4 -0
  111. flwr/proto/common_pb2_grpc.pyi +4 -0
  112. flwr/proto/driver_pb2.py +26 -19
  113. flwr/proto/driver_pb2.pyi +34 -0
  114. flwr/proto/driver_pb2_grpc.py +70 -0
  115. flwr/proto/driver_pb2_grpc.pyi +28 -0
  116. flwr/proto/exec_pb2.py +43 -0
  117. flwr/proto/exec_pb2.pyi +95 -0
  118. flwr/proto/exec_pb2_grpc.py +101 -0
  119. flwr/proto/exec_pb2_grpc.pyi +41 -0
  120. flwr/proto/fab_pb2.py +30 -0
  121. flwr/proto/fab_pb2.pyi +56 -0
  122. flwr/proto/fab_pb2_grpc.py +4 -0
  123. flwr/proto/fab_pb2_grpc.pyi +4 -0
  124. flwr/proto/fleet_pb2.py +29 -23
  125. flwr/proto/fleet_pb2.pyi +33 -0
  126. flwr/proto/fleet_pb2_grpc.py +102 -0
  127. flwr/proto/fleet_pb2_grpc.pyi +35 -0
  128. flwr/proto/grpcadapter_pb2.py +32 -0
  129. flwr/proto/grpcadapter_pb2.pyi +43 -0
  130. flwr/proto/grpcadapter_pb2_grpc.py +66 -0
  131. flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
  132. flwr/proto/message_pb2.py +41 -0
  133. flwr/proto/message_pb2.pyi +122 -0
  134. flwr/proto/message_pb2_grpc.py +4 -0
  135. flwr/proto/message_pb2_grpc.pyi +4 -0
  136. flwr/proto/run_pb2.py +35 -0
  137. flwr/proto/run_pb2.pyi +76 -0
  138. flwr/proto/run_pb2_grpc.py +4 -0
  139. flwr/proto/run_pb2_grpc.pyi +4 -0
  140. flwr/proto/task_pb2.py +7 -8
  141. flwr/proto/task_pb2.pyi +8 -5
  142. flwr/server/__init__.py +4 -8
  143. flwr/server/app.py +298 -350
  144. flwr/server/compat/app.py +6 -57
  145. flwr/server/compat/app_utils.py +5 -4
  146. flwr/server/compat/driver_client_proxy.py +29 -48
  147. flwr/server/compat/legacy_context.py +5 -4
  148. flwr/server/driver/__init__.py +2 -0
  149. flwr/server/driver/driver.py +22 -132
  150. flwr/server/driver/grpc_driver.py +224 -74
  151. flwr/server/driver/inmemory_driver.py +183 -0
  152. flwr/server/history.py +20 -20
  153. flwr/server/run_serverapp.py +121 -34
  154. flwr/server/server.py +11 -7
  155. flwr/server/server_app.py +59 -10
  156. flwr/server/serverapp_components.py +52 -0
  157. flwr/server/strategy/__init__.py +2 -2
  158. flwr/server/strategy/bulyan.py +1 -1
  159. flwr/server/strategy/dp_adaptive_clipping.py +3 -3
  160. flwr/server/strategy/dp_fixed_clipping.py +4 -3
  161. flwr/server/strategy/dpfedavg_adaptive.py +1 -1
  162. flwr/server/strategy/dpfedavg_fixed.py +1 -1
  163. flwr/server/strategy/fedadagrad.py +1 -1
  164. flwr/server/strategy/fedadam.py +1 -1
  165. flwr/server/strategy/fedavg_android.py +1 -1
  166. flwr/server/strategy/fedavgm.py +1 -1
  167. flwr/server/strategy/fedmedian.py +1 -1
  168. flwr/server/strategy/fedopt.py +1 -1
  169. flwr/server/strategy/fedprox.py +1 -1
  170. flwr/server/strategy/fedxgb_bagging.py +1 -1
  171. flwr/server/strategy/fedxgb_cyclic.py +1 -1
  172. flwr/server/strategy/fedxgb_nn_avg.py +1 -1
  173. flwr/server/strategy/fedyogi.py +1 -1
  174. flwr/server/strategy/krum.py +1 -1
  175. flwr/server/strategy/qfedavg.py +1 -1
  176. flwr/server/superlink/driver/__init__.py +1 -1
  177. flwr/server/superlink/driver/driver_grpc.py +1 -1
  178. flwr/server/superlink/driver/driver_servicer.py +51 -4
  179. flwr/server/superlink/ffs/__init__.py +24 -0
  180. flwr/server/superlink/ffs/disk_ffs.py +104 -0
  181. flwr/server/superlink/ffs/ffs.py +79 -0
  182. flwr/server/superlink/fleet/__init__.py +1 -1
  183. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  184. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +131 -0
  185. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  186. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
  187. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
  188. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  189. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +8 -2
  190. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  191. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +30 -2
  192. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +214 -0
  193. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  194. flwr/server/superlink/fleet/message_handler/message_handler.py +42 -2
  195. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  196. flwr/server/superlink/fleet/rest_rere/rest_api.py +59 -1
  197. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
  198. flwr/server/superlink/fleet/vce/backend/backend.py +5 -5
  199. flwr/server/superlink/fleet/vce/backend/raybackend.py +53 -56
  200. flwr/server/superlink/fleet/vce/vce_api.py +190 -127
  201. flwr/server/superlink/state/__init__.py +1 -1
  202. flwr/server/superlink/state/in_memory_state.py +159 -42
  203. flwr/server/superlink/state/sqlite_state.py +243 -39
  204. flwr/server/superlink/state/state.py +81 -6
  205. flwr/server/superlink/state/state_factory.py +11 -2
  206. flwr/server/superlink/state/utils.py +62 -0
  207. flwr/server/typing.py +2 -0
  208. flwr/server/utils/__init__.py +1 -1
  209. flwr/server/utils/tensorboard.py +1 -1
  210. flwr/server/utils/validator.py +23 -9
  211. flwr/server/workflow/default_workflows.py +67 -25
  212. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +18 -6
  213. flwr/simulation/__init__.py +7 -4
  214. flwr/simulation/app.py +67 -36
  215. flwr/simulation/ray_transport/__init__.py +1 -1
  216. flwr/simulation/ray_transport/ray_actor.py +20 -46
  217. flwr/simulation/ray_transport/ray_client_proxy.py +36 -16
  218. flwr/simulation/run_simulation.py +308 -92
  219. flwr/superexec/__init__.py +21 -0
  220. flwr/superexec/app.py +184 -0
  221. flwr/superexec/deployment.py +185 -0
  222. flwr/superexec/exec_grpc.py +55 -0
  223. flwr/superexec/exec_servicer.py +70 -0
  224. flwr/superexec/executor.py +75 -0
  225. flwr/superexec/simulation.py +193 -0
  226. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/METADATA +10 -6
  227. flwr_nightly-1.11.0.dev20240813.dist-info/RECORD +288 -0
  228. flwr_nightly-1.11.0.dev20240813.dist-info/entry_points.txt +10 -0
  229. flwr/cli/flower_toml.py +0 -140
  230. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  231. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  232. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  233. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  234. flwr_nightly-1.8.0.dev20240315.dist-info/RECORD +0 -211
  235. flwr_nightly-1.8.0.dev20240315.dist-info/entry_points.txt +0 -9
  236. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/LICENSE +0 -0
  237. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.11.0.dev20240813.dist-info}/WHEEL +0 -0
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -15,13 +15,15 @@
15
15
  """Driver API servicer."""
16
16
 
17
17
 
18
- from logging import DEBUG, INFO
18
+ import time
19
+ from logging import DEBUG
19
20
  from typing import List, Optional, Set
20
21
  from uuid import UUID
21
22
 
22
23
  import grpc
23
24
 
24
25
  from flwr.common.logger import log
26
+ from flwr.common.serde import user_config_from_proto, user_config_to_proto
25
27
  from flwr.proto import driver_pb2_grpc # pylint: disable=E0611
26
28
  from flwr.proto.driver_pb2 import ( # pylint: disable=E0611
27
29
  CreateRunRequest,
@@ -33,7 +35,13 @@ from flwr.proto.driver_pb2 import ( # pylint: disable=E0611
33
35
  PushTaskInsRequest,
34
36
  PushTaskInsResponse,
35
37
  )
38
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
36
39
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
40
+ from flwr.proto.run_pb2 import ( # pylint: disable=E0611
41
+ GetRunRequest,
42
+ GetRunResponse,
43
+ Run,
44
+ )
37
45
  from flwr.proto.task_pb2 import TaskRes # pylint: disable=E0611
38
46
  from flwr.server.superlink.state import State, StateFactory
39
47
  from flwr.server.utils.validator import validate_task_ins_or_res
@@ -61,9 +69,13 @@ class DriverServicer(driver_pb2_grpc.DriverServicer):
61
69
  self, request: CreateRunRequest, context: grpc.ServicerContext
62
70
  ) -> CreateRunResponse:
63
71
  """Create run ID."""
64
- log(INFO, "DriverServicer.CreateRun")
72
+ log(DEBUG, "DriverServicer.CreateRun")
65
73
  state: State = self.state_factory.state()
66
- run_id = state.create_run()
74
+ run_id = state.create_run(
75
+ request.fab_id,
76
+ request.fab_version,
77
+ user_config_from_proto(request.override_config),
78
+ )
67
79
  return CreateRunResponse(run_id=run_id)
68
80
 
69
81
  def PushTaskIns(
@@ -72,6 +84,11 @@ class DriverServicer(driver_pb2_grpc.DriverServicer):
72
84
  """Push a set of TaskIns."""
73
85
  log(DEBUG, "DriverServicer.PushTaskIns")
74
86
 
87
+ # Set pushed_at (timestamp in seconds)
88
+ pushed_at = time.time()
89
+ for task_ins in request.task_ins_list:
90
+ task_ins.task.pushed_at = pushed_at
91
+
75
92
  # Validate request
76
93
  _raise_if(len(request.task_ins_list) == 0, "`task_ins_list` must not be empty")
77
94
  for task_ins in request.task_ins_list:
@@ -123,6 +140,36 @@ class DriverServicer(driver_pb2_grpc.DriverServicer):
123
140
  context.set_code(grpc.StatusCode.OK)
124
141
  return PullTaskResResponse(task_res_list=task_res_list)
125
142
 
143
+ def GetRun(
144
+ self, request: GetRunRequest, context: grpc.ServicerContext
145
+ ) -> GetRunResponse:
146
+ """Get run information."""
147
+ log(DEBUG, "DriverServicer.GetRun")
148
+
149
+ # Init state
150
+ state: State = self.state_factory.state()
151
+
152
+ # Retrieve run information
153
+ run = state.get_run(request.run_id)
154
+
155
+ if run is None:
156
+ return GetRunResponse()
157
+
158
+ return GetRunResponse(
159
+ run=Run(
160
+ run_id=run.run_id,
161
+ fab_id=run.fab_id,
162
+ fab_version=run.fab_version,
163
+ override_config=user_config_to_proto(run.override_config),
164
+ )
165
+ )
166
+
167
+ def GetFab(
168
+ self, request: GetFabRequest, context: grpc.ServicerContext
169
+ ) -> GetFabResponse:
170
+ """Will be implemented later."""
171
+ raise NotImplementedError
172
+
126
173
 
127
174
  def _raise_if(validation_error: bool, detail: str) -> None:
128
175
  if validation_error:
@@ -0,0 +1,24 @@
1
+ # Copyright 2024 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 File Storage for large objects."""
16
+
17
+
18
+ from .disk_ffs import DiskFfs as DiskFfs
19
+ from .ffs import Ffs as Ffs
20
+
21
+ __all__ = [
22
+ "DiskFfs",
23
+ "Ffs",
24
+ ]
@@ -0,0 +1,104 @@
1
+ # Copyright 2024 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
+ """Disk based Flower File Storage."""
16
+
17
+ import hashlib
18
+ import json
19
+ from pathlib import Path
20
+ from typing import Dict, List, Tuple
21
+
22
+ from flwr.server.superlink.ffs.ffs import Ffs
23
+
24
+
25
+ class DiskFfs(Ffs): # pylint: disable=R0904
26
+ """Disk-based Flower File Storage interface for large objects."""
27
+
28
+ def __init__(self, base_dir: str) -> None:
29
+ """Create a new DiskFfs instance.
30
+
31
+ Parameters
32
+ ----------
33
+ base_dir : str
34
+ The base directory to store the objects.
35
+ """
36
+ self.base_dir = Path(base_dir)
37
+
38
+ def put(self, content: bytes, meta: Dict[str, str]) -> str:
39
+ """Store bytes and metadata and return key (hash of content).
40
+
41
+ Parameters
42
+ ----------
43
+ content : bytes
44
+ The content to be stored.
45
+ meta : Dict[str, str]
46
+ The metadata to be stored.
47
+
48
+ Returns
49
+ -------
50
+ key : str
51
+ The key (sha256hex hash) of the content.
52
+ """
53
+ content_hash = hashlib.sha256(content).hexdigest()
54
+
55
+ self.base_dir.mkdir(exist_ok=True, parents=True)
56
+ (self.base_dir / content_hash).write_bytes(content)
57
+ (self.base_dir / f"{content_hash}.META").write_text(json.dumps(meta))
58
+
59
+ return content_hash
60
+
61
+ def get(self, key: str) -> Tuple[bytes, Dict[str, str]]:
62
+ """Return tuple containing the object content and metadata.
63
+
64
+ Parameters
65
+ ----------
66
+ key : str
67
+ The sha256hex hash of the object to be retrieved.
68
+
69
+ Returns
70
+ -------
71
+ Tuple[bytes, Dict[str, str]]
72
+ A tuple containing the object content and metadata.
73
+ """
74
+ content = (self.base_dir / key).read_bytes()
75
+ meta = json.loads((self.base_dir / f"{key}.META").read_text())
76
+
77
+ return content, meta
78
+
79
+ def delete(self, key: str) -> None:
80
+ """Delete object with hash.
81
+
82
+ Parameters
83
+ ----------
84
+ key : str
85
+ The sha256hex hash of the object to be deleted.
86
+ """
87
+ (self.base_dir / key).unlink()
88
+ (self.base_dir / f"{key}.META").unlink()
89
+
90
+ def list(self) -> List[str]:
91
+ """List all keys.
92
+
93
+ Return all available keys in this `Ffs` instance.
94
+ This can be combined with, for example,
95
+ the `delete` method to delete objects.
96
+
97
+ Returns
98
+ -------
99
+ List[str]
100
+ A list of all available keys.
101
+ """
102
+ return [
103
+ item.name for item in self.base_dir.iterdir() if not item.suffix == ".META"
104
+ ]
@@ -0,0 +1,79 @@
1
+ # Copyright 2024 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 for Flower File Storage interface."""
16
+
17
+
18
+ import abc
19
+ from typing import Dict, List, Tuple
20
+
21
+
22
+ class Ffs(abc.ABC): # pylint: disable=R0904
23
+ """Abstract Flower File Storage interface for large objects."""
24
+
25
+ @abc.abstractmethod
26
+ def put(self, content: bytes, meta: Dict[str, str]) -> str:
27
+ """Store bytes and metadata and return sha256hex hash of data as str.
28
+
29
+ Parameters
30
+ ----------
31
+ content : bytes
32
+ The content to be stored.
33
+ meta : Dict[str, str]
34
+ The metadata to be stored.
35
+
36
+ Returns
37
+ -------
38
+ key : str
39
+ The key (sha256hex hash) of the content.
40
+ """
41
+
42
+ @abc.abstractmethod
43
+ def get(self, key: str) -> Tuple[bytes, Dict[str, str]]:
44
+ """Return tuple containing the object content and metadata.
45
+
46
+ Parameters
47
+ ----------
48
+ key : str
49
+ The key (sha256hex hash) of the object to be retrieved.
50
+
51
+ Returns
52
+ -------
53
+ Tuple[bytes, Dict[str, str]]
54
+ A tuple containing the object content and metadata.
55
+ """
56
+
57
+ @abc.abstractmethod
58
+ def delete(self, key: str) -> None:
59
+ """Delete object with hash.
60
+
61
+ Parameters
62
+ ----------
63
+ key : str
64
+ The key (sha256hex hash) of the object to be deleted.
65
+ """
66
+
67
+ @abc.abstractmethod
68
+ def list(self) -> List[str]:
69
+ """List keys of all stored objects.
70
+
71
+ Return all available keys in this `Ffs` instance.
72
+ This can be combined with, for example,
73
+ the `delete` method to delete objects.
74
+
75
+ Returns
76
+ -------
77
+ List[str]
78
+ A list of all available keys.
79
+ """
@@ -1,4 +1,4 @@
1
- # Copyright 2022 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -0,0 +1,15 @@
1
+ # Copyright 2024 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
+ """Server-side part of the GrpcAdapter transport layer."""
@@ -0,0 +1,131 @@
1
+ # Copyright 2024 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
+ """Fleet API gRPC adapter servicer."""
16
+
17
+
18
+ from logging import DEBUG, INFO
19
+ from typing import Callable, Type, TypeVar
20
+
21
+ import grpc
22
+ from google.protobuf.message import Message as GrpcMessage
23
+
24
+ from flwr.common.logger import log
25
+ from flwr.proto import grpcadapter_pb2_grpc # pylint: disable=E0611
26
+ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
27
+ CreateNodeRequest,
28
+ CreateNodeResponse,
29
+ DeleteNodeRequest,
30
+ DeleteNodeResponse,
31
+ PingRequest,
32
+ PingResponse,
33
+ PullTaskInsRequest,
34
+ PullTaskInsResponse,
35
+ PushTaskResRequest,
36
+ PushTaskResResponse,
37
+ )
38
+ from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
39
+ from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
40
+ from flwr.server.superlink.fleet.message_handler import message_handler
41
+ from flwr.server.superlink.state import StateFactory
42
+
43
+ T = TypeVar("T", bound=GrpcMessage)
44
+
45
+
46
+ def _handle(
47
+ msg_container: MessageContainer,
48
+ request_type: Type[T],
49
+ handler: Callable[[T], GrpcMessage],
50
+ ) -> MessageContainer:
51
+ req = request_type.FromString(msg_container.grpc_message_content)
52
+ res = handler(req)
53
+ return MessageContainer(
54
+ metadata={},
55
+ grpc_message_name=res.__class__.__qualname__,
56
+ grpc_message_content=res.SerializeToString(),
57
+ )
58
+
59
+
60
+ class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer):
61
+ """Fleet API via GrpcAdapter servicer."""
62
+
63
+ def __init__(self, state_factory: StateFactory) -> None:
64
+ self.state_factory = state_factory
65
+
66
+ def SendReceive(
67
+ self, request: MessageContainer, context: grpc.ServicerContext
68
+ ) -> MessageContainer:
69
+ """."""
70
+ log(DEBUG, "GrpcAdapterServicer.SendReceive")
71
+ if request.grpc_message_name == CreateNodeRequest.__qualname__:
72
+ return _handle(request, CreateNodeRequest, self._create_node)
73
+ if request.grpc_message_name == DeleteNodeRequest.__qualname__:
74
+ return _handle(request, DeleteNodeRequest, self._delete_node)
75
+ if request.grpc_message_name == PingRequest.__qualname__:
76
+ return _handle(request, PingRequest, self._ping)
77
+ if request.grpc_message_name == PullTaskInsRequest.__qualname__:
78
+ return _handle(request, PullTaskInsRequest, self._pull_task_ins)
79
+ if request.grpc_message_name == PushTaskResRequest.__qualname__:
80
+ return _handle(request, PushTaskResRequest, self._push_task_res)
81
+ if request.grpc_message_name == GetRunRequest.__qualname__:
82
+ return _handle(request, GetRunRequest, self._get_run)
83
+ raise ValueError(f"Invalid grpc_message_name: {request.grpc_message_name}")
84
+
85
+ def _create_node(self, request: CreateNodeRequest) -> CreateNodeResponse:
86
+ """."""
87
+ log(INFO, "GrpcAdapter.CreateNode")
88
+ return message_handler.create_node(
89
+ request=request,
90
+ state=self.state_factory.state(),
91
+ )
92
+
93
+ def _delete_node(self, request: DeleteNodeRequest) -> DeleteNodeResponse:
94
+ """."""
95
+ log(INFO, "GrpcAdapter.DeleteNode")
96
+ return message_handler.delete_node(
97
+ request=request,
98
+ state=self.state_factory.state(),
99
+ )
100
+
101
+ def _ping(self, request: PingRequest) -> PingResponse:
102
+ """."""
103
+ log(DEBUG, "GrpcAdapter.Ping")
104
+ return message_handler.ping(
105
+ request=request,
106
+ state=self.state_factory.state(),
107
+ )
108
+
109
+ def _pull_task_ins(self, request: PullTaskInsRequest) -> PullTaskInsResponse:
110
+ """Pull TaskIns."""
111
+ log(INFO, "GrpcAdapter.PullTaskIns")
112
+ return message_handler.pull_task_ins(
113
+ request=request,
114
+ state=self.state_factory.state(),
115
+ )
116
+
117
+ def _push_task_res(self, request: PushTaskResRequest) -> PushTaskResResponse:
118
+ """Push TaskRes."""
119
+ log(INFO, "GrpcAdapter.PushTaskRes")
120
+ return message_handler.push_task_res(
121
+ request=request,
122
+ state=self.state_factory.state(),
123
+ )
124
+
125
+ def _get_run(self, request: GetRunRequest) -> GetRunResponse:
126
+ """Get run information."""
127
+ log(INFO, "GrpcAdapter.GetRun")
128
+ return message_handler.get_run(
129
+ request=request,
130
+ state=self.state_factory.state(),
131
+ )
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
18
18
  import concurrent.futures
19
19
  import sys
20
20
  from logging import ERROR
21
- from typing import Any, Callable, Optional, Tuple, Union
21
+ from typing import Any, Callable, Optional, Sequence, Tuple, Union
22
22
 
23
23
  import grpc
24
24
 
@@ -29,6 +29,9 @@ from flwr.proto.transport_pb2_grpc import ( # pylint: disable=E0611
29
29
  )
30
30
  from flwr.server.client_manager import ClientManager
31
31
  from flwr.server.superlink.driver.driver_servicer import DriverServicer
32
+ from flwr.server.superlink.fleet.grpc_adapter.grpc_adapter_servicer import (
33
+ GrpcAdapterServicer,
34
+ )
32
35
  from flwr.server.superlink.fleet.grpc_bidi.flower_service_servicer import (
33
36
  FlowerServiceServicer,
34
37
  )
@@ -154,6 +157,7 @@ def start_grpc_server( # pylint: disable=too-many-arguments
154
157
  def generic_create_grpc_server( # pylint: disable=too-many-arguments
155
158
  servicer_and_add_fn: Union[
156
159
  Tuple[FleetServicer, AddServicerToServerFn],
160
+ Tuple[GrpcAdapterServicer, AddServicerToServerFn],
157
161
  Tuple[FlowerServiceServicer, AddServicerToServerFn],
158
162
  Tuple[DriverServicer, AddServicerToServerFn],
159
163
  ],
@@ -162,6 +166,7 @@ def generic_create_grpc_server( # pylint: disable=too-many-arguments
162
166
  max_message_length: int = GRPC_MAX_MESSAGE_LENGTH,
163
167
  keepalive_time_ms: int = 210000,
164
168
  certificates: Optional[Tuple[bytes, bytes, bytes]] = None,
169
+ interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
165
170
  ) -> grpc.Server:
166
171
  """Create a gRPC server with a single servicer.
167
172
 
@@ -249,6 +254,7 @@ def generic_create_grpc_server( # pylint: disable=too-many-arguments
249
254
  # returning RESOURCE_EXHAUSTED status, or None to indicate no limit.
250
255
  maximum_concurrent_rpcs=max_concurrent_workers,
251
256
  options=options,
257
+ interceptors=interceptors,
252
258
  )
253
259
  add_servicer_to_server_fn(servicer, server)
254
260
 
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -15,22 +15,26 @@
15
15
  """Fleet API gRPC request-response servicer."""
16
16
 
17
17
 
18
- from logging import INFO
18
+ from logging import DEBUG, INFO
19
19
 
20
20
  import grpc
21
21
 
22
22
  from flwr.common.logger import log
23
23
  from flwr.proto import fleet_pb2_grpc # pylint: disable=E0611
24
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
24
25
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
25
26
  CreateNodeRequest,
26
27
  CreateNodeResponse,
27
28
  DeleteNodeRequest,
28
29
  DeleteNodeResponse,
30
+ PingRequest,
31
+ PingResponse,
29
32
  PullTaskInsRequest,
30
33
  PullTaskInsResponse,
31
34
  PushTaskResRequest,
32
35
  PushTaskResResponse,
33
36
  )
37
+ from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
34
38
  from flwr.server.superlink.fleet.message_handler import message_handler
35
39
  from flwr.server.superlink.state import StateFactory
36
40
 
@@ -61,6 +65,14 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
61
65
  state=self.state_factory.state(),
62
66
  )
63
67
 
68
+ def Ping(self, request: PingRequest, context: grpc.ServicerContext) -> PingResponse:
69
+ """."""
70
+ log(DEBUG, "FleetServicer.Ping")
71
+ return message_handler.ping(
72
+ request=request,
73
+ state=self.state_factory.state(),
74
+ )
75
+
64
76
  def PullTaskIns(
65
77
  self, request: PullTaskInsRequest, context: grpc.ServicerContext
66
78
  ) -> PullTaskInsResponse:
@@ -80,3 +92,19 @@ class FleetServicer(fleet_pb2_grpc.FleetServicer):
80
92
  request=request,
81
93
  state=self.state_factory.state(),
82
94
  )
95
+
96
+ def GetRun(
97
+ self, request: GetRunRequest, context: grpc.ServicerContext
98
+ ) -> GetRunResponse:
99
+ """Get run information."""
100
+ log(INFO, "FleetServicer.GetRun")
101
+ return message_handler.get_run(
102
+ request=request,
103
+ state=self.state_factory.state(),
104
+ )
105
+
106
+ def GetFab(
107
+ self, request: GetFabRequest, context: grpc.ServicerContext
108
+ ) -> GetFabResponse:
109
+ """Will be implemented later."""
110
+ raise NotImplementedError