flwr-nightly 1.8.0.dev20240315__py3-none-any.whl → 1.15.0.dev20250114__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (311) hide show
  1. flwr/cli/app.py +16 -2
  2. flwr/cli/build.py +181 -0
  3. flwr/cli/cli_user_auth_interceptor.py +90 -0
  4. flwr/cli/config_utils.py +343 -0
  5. flwr/cli/example.py +4 -1
  6. flwr/cli/install.py +253 -0
  7. flwr/cli/log.py +182 -0
  8. flwr/{server/superlink/state → cli/login}/__init__.py +4 -10
  9. flwr/cli/login/login.py +88 -0
  10. flwr/cli/ls.py +327 -0
  11. flwr/cli/new/__init__.py +1 -0
  12. flwr/cli/new/new.py +210 -66
  13. flwr/cli/new/templates/app/.gitignore.tpl +163 -0
  14. flwr/cli/new/templates/app/LICENSE.tpl +202 -0
  15. flwr/cli/new/templates/app/README.baseline.md.tpl +127 -0
  16. flwr/cli/new/templates/app/README.flowertune.md.tpl +66 -0
  17. flwr/cli/new/templates/app/README.md.tpl +16 -32
  18. flwr/cli/new/templates/app/code/__init__.baseline.py.tpl +1 -0
  19. flwr/cli/new/templates/app/code/__init__.py.tpl +1 -1
  20. flwr/cli/new/templates/app/code/client.baseline.py.tpl +58 -0
  21. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +55 -0
  22. flwr/cli/new/templates/app/code/client.jax.py.tpl +50 -0
  23. flwr/cli/new/templates/app/code/client.mlx.py.tpl +73 -0
  24. flwr/cli/new/templates/app/code/client.numpy.py.tpl +7 -7
  25. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +30 -21
  26. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +63 -0
  27. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +57 -1
  28. flwr/cli/new/templates/app/code/dataset.baseline.py.tpl +36 -0
  29. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +15 -0
  30. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +126 -0
  31. flwr/cli/new/templates/app/code/flwr_tune/dataset.py.tpl +87 -0
  32. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +78 -0
  33. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +94 -0
  34. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +83 -0
  35. flwr/cli/new/templates/app/code/model.baseline.py.tpl +80 -0
  36. flwr/cli/new/templates/app/code/server.baseline.py.tpl +46 -0
  37. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +38 -0
  38. flwr/cli/new/templates/app/code/server.jax.py.tpl +26 -0
  39. flwr/cli/new/templates/app/code/server.mlx.py.tpl +31 -0
  40. flwr/cli/new/templates/app/code/server.numpy.py.tpl +22 -9
  41. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +21 -18
  42. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +36 -0
  43. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +29 -1
  44. flwr/cli/new/templates/app/code/strategy.baseline.py.tpl +1 -0
  45. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +102 -0
  46. flwr/cli/new/templates/app/code/task.jax.py.tpl +57 -0
  47. flwr/cli/new/templates/app/code/task.mlx.py.tpl +102 -0
  48. flwr/cli/new/templates/app/code/task.numpy.py.tpl +7 -0
  49. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +29 -24
  50. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +67 -0
  51. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +53 -0
  52. flwr/cli/new/templates/app/code/utils.baseline.py.tpl +1 -0
  53. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +138 -0
  54. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +68 -0
  55. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +46 -0
  56. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +35 -0
  57. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +39 -0
  58. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +25 -12
  59. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +29 -14
  60. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +35 -0
  61. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +29 -14
  62. flwr/cli/run/__init__.py +1 -0
  63. flwr/cli/run/run.py +212 -34
  64. flwr/cli/stop.py +130 -0
  65. flwr/cli/utils.py +240 -5
  66. flwr/client/__init__.py +3 -2
  67. flwr/client/app.py +432 -255
  68. flwr/client/client.py +1 -11
  69. flwr/client/client_app.py +74 -13
  70. flwr/client/clientapp/__init__.py +22 -0
  71. flwr/client/clientapp/app.py +259 -0
  72. flwr/client/clientapp/clientappio_servicer.py +244 -0
  73. flwr/client/clientapp/utils.py +115 -0
  74. flwr/client/dpfedavg_numpy_client.py +7 -8
  75. flwr/client/grpc_adapter_client/__init__.py +15 -0
  76. flwr/client/grpc_adapter_client/connection.py +98 -0
  77. flwr/client/grpc_client/connection.py +21 -7
  78. flwr/client/grpc_rere_client/__init__.py +1 -1
  79. flwr/client/grpc_rere_client/client_interceptor.py +176 -0
  80. flwr/client/grpc_rere_client/connection.py +163 -56
  81. flwr/client/grpc_rere_client/grpc_adapter.py +167 -0
  82. flwr/client/heartbeat.py +74 -0
  83. flwr/client/message_handler/__init__.py +1 -1
  84. flwr/client/message_handler/message_handler.py +10 -11
  85. flwr/client/mod/__init__.py +5 -5
  86. flwr/client/mod/centraldp_mods.py +4 -2
  87. flwr/client/mod/comms_mods.py +5 -4
  88. flwr/client/mod/localdp_mod.py +10 -5
  89. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  90. flwr/client/mod/secure_aggregation/secaggplus_mod.py +26 -26
  91. flwr/client/mod/utils.py +2 -4
  92. flwr/client/nodestate/__init__.py +26 -0
  93. flwr/client/nodestate/in_memory_nodestate.py +38 -0
  94. flwr/client/nodestate/nodestate.py +31 -0
  95. flwr/client/nodestate/nodestate_factory.py +38 -0
  96. flwr/client/numpy_client.py +8 -31
  97. flwr/client/rest_client/__init__.py +1 -1
  98. flwr/client/rest_client/connection.py +199 -176
  99. flwr/client/run_info_store.py +112 -0
  100. flwr/client/supernode/__init__.py +24 -0
  101. flwr/client/supernode/app.py +321 -0
  102. flwr/client/typing.py +1 -0
  103. flwr/common/__init__.py +17 -11
  104. flwr/common/address.py +47 -3
  105. flwr/common/args.py +153 -0
  106. flwr/common/auth_plugin/__init__.py +24 -0
  107. flwr/common/auth_plugin/auth_plugin.py +121 -0
  108. flwr/common/config.py +243 -0
  109. flwr/common/constant.py +132 -1
  110. flwr/common/context.py +32 -2
  111. flwr/common/date.py +22 -4
  112. flwr/common/differential_privacy.py +2 -2
  113. flwr/common/dp.py +2 -4
  114. flwr/common/exit_handlers.py +3 -3
  115. flwr/common/grpc.py +164 -5
  116. flwr/common/logger.py +230 -12
  117. flwr/common/message.py +191 -106
  118. flwr/common/object_ref.py +179 -44
  119. flwr/common/pyproject.py +1 -0
  120. flwr/common/record/__init__.py +2 -1
  121. flwr/common/record/configsrecord.py +58 -18
  122. flwr/common/record/metricsrecord.py +57 -17
  123. flwr/common/record/parametersrecord.py +88 -20
  124. flwr/common/record/recordset.py +153 -30
  125. flwr/common/record/typeddict.py +30 -55
  126. flwr/common/recordset_compat.py +31 -12
  127. flwr/common/retry_invoker.py +123 -30
  128. flwr/common/secure_aggregation/__init__.py +1 -1
  129. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  130. flwr/common/secure_aggregation/crypto/shamir.py +11 -11
  131. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +68 -4
  132. flwr/common/secure_aggregation/ndarrays_arithmetic.py +17 -17
  133. flwr/common/secure_aggregation/quantization.py +8 -8
  134. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  135. flwr/common/secure_aggregation/secaggplus_utils.py +10 -12
  136. flwr/common/serde.py +298 -19
  137. flwr/common/telemetry.py +65 -29
  138. flwr/common/typing.py +120 -19
  139. flwr/common/version.py +17 -3
  140. flwr/proto/clientappio_pb2.py +45 -0
  141. flwr/proto/clientappio_pb2.pyi +132 -0
  142. flwr/proto/clientappio_pb2_grpc.py +135 -0
  143. flwr/proto/clientappio_pb2_grpc.pyi +53 -0
  144. flwr/proto/exec_pb2.py +62 -0
  145. flwr/proto/exec_pb2.pyi +212 -0
  146. flwr/proto/exec_pb2_grpc.py +237 -0
  147. flwr/proto/exec_pb2_grpc.pyi +93 -0
  148. flwr/proto/fab_pb2.py +31 -0
  149. flwr/proto/fab_pb2.pyi +65 -0
  150. flwr/proto/fab_pb2_grpc.py +4 -0
  151. flwr/proto/fab_pb2_grpc.pyi +4 -0
  152. flwr/proto/fleet_pb2.py +42 -23
  153. flwr/proto/fleet_pb2.pyi +123 -1
  154. flwr/proto/fleet_pb2_grpc.py +170 -0
  155. flwr/proto/fleet_pb2_grpc.pyi +61 -0
  156. flwr/proto/grpcadapter_pb2.py +32 -0
  157. flwr/proto/grpcadapter_pb2.pyi +43 -0
  158. flwr/proto/grpcadapter_pb2_grpc.py +66 -0
  159. flwr/proto/grpcadapter_pb2_grpc.pyi +24 -0
  160. flwr/proto/log_pb2.py +29 -0
  161. flwr/proto/log_pb2.pyi +39 -0
  162. flwr/proto/log_pb2_grpc.py +4 -0
  163. flwr/proto/log_pb2_grpc.pyi +4 -0
  164. flwr/proto/message_pb2.py +41 -0
  165. flwr/proto/message_pb2.pyi +128 -0
  166. flwr/proto/message_pb2_grpc.py +4 -0
  167. flwr/proto/message_pb2_grpc.pyi +4 -0
  168. flwr/proto/node_pb2.py +1 -1
  169. flwr/proto/recordset_pb2.py +35 -33
  170. flwr/proto/recordset_pb2.pyi +40 -14
  171. flwr/proto/run_pb2.py +64 -0
  172. flwr/proto/run_pb2.pyi +268 -0
  173. flwr/proto/run_pb2_grpc.py +4 -0
  174. flwr/proto/run_pb2_grpc.pyi +4 -0
  175. flwr/proto/serverappio_pb2.py +52 -0
  176. flwr/proto/{driver_pb2.pyi → serverappio_pb2.pyi} +62 -20
  177. flwr/proto/serverappio_pb2_grpc.py +410 -0
  178. flwr/proto/serverappio_pb2_grpc.pyi +160 -0
  179. flwr/proto/simulationio_pb2.py +38 -0
  180. flwr/proto/simulationio_pb2.pyi +65 -0
  181. flwr/proto/simulationio_pb2_grpc.py +239 -0
  182. flwr/proto/simulationio_pb2_grpc.pyi +94 -0
  183. flwr/proto/task_pb2.py +7 -8
  184. flwr/proto/task_pb2.pyi +8 -5
  185. flwr/proto/transport_pb2.py +8 -8
  186. flwr/proto/transport_pb2.pyi +9 -6
  187. flwr/server/__init__.py +2 -10
  188. flwr/server/app.py +579 -402
  189. flwr/server/client_manager.py +8 -6
  190. flwr/server/compat/app.py +6 -62
  191. flwr/server/compat/app_utils.py +14 -8
  192. flwr/server/compat/driver_client_proxy.py +25 -58
  193. flwr/server/compat/legacy_context.py +5 -4
  194. flwr/server/driver/__init__.py +2 -0
  195. flwr/server/driver/driver.py +36 -131
  196. flwr/server/driver/grpc_driver.py +217 -81
  197. flwr/server/driver/inmemory_driver.py +182 -0
  198. flwr/server/history.py +28 -29
  199. flwr/server/run_serverapp.py +15 -126
  200. flwr/server/server.py +50 -44
  201. flwr/server/server_app.py +59 -10
  202. flwr/server/serverapp/__init__.py +22 -0
  203. flwr/server/serverapp/app.py +256 -0
  204. flwr/server/serverapp_components.py +52 -0
  205. flwr/server/strategy/__init__.py +2 -2
  206. flwr/server/strategy/aggregate.py +37 -23
  207. flwr/server/strategy/bulyan.py +9 -9
  208. flwr/server/strategy/dp_adaptive_clipping.py +25 -25
  209. flwr/server/strategy/dp_fixed_clipping.py +23 -22
  210. flwr/server/strategy/dpfedavg_adaptive.py +8 -8
  211. flwr/server/strategy/dpfedavg_fixed.py +13 -12
  212. flwr/server/strategy/fault_tolerant_fedavg.py +11 -11
  213. flwr/server/strategy/fedadagrad.py +9 -9
  214. flwr/server/strategy/fedadam.py +20 -10
  215. flwr/server/strategy/fedavg.py +16 -16
  216. flwr/server/strategy/fedavg_android.py +17 -17
  217. flwr/server/strategy/fedavgm.py +9 -9
  218. flwr/server/strategy/fedmedian.py +5 -5
  219. flwr/server/strategy/fedopt.py +6 -6
  220. flwr/server/strategy/fedprox.py +7 -7
  221. flwr/server/strategy/fedtrimmedavg.py +8 -8
  222. flwr/server/strategy/fedxgb_bagging.py +12 -12
  223. flwr/server/strategy/fedxgb_cyclic.py +10 -10
  224. flwr/server/strategy/fedxgb_nn_avg.py +6 -6
  225. flwr/server/strategy/fedyogi.py +9 -9
  226. flwr/server/strategy/krum.py +9 -9
  227. flwr/server/strategy/qfedavg.py +16 -16
  228. flwr/server/strategy/strategy.py +10 -10
  229. flwr/server/superlink/driver/__init__.py +2 -2
  230. flwr/server/superlink/driver/serverappio_grpc.py +61 -0
  231. flwr/server/superlink/driver/serverappio_servicer.py +363 -0
  232. flwr/server/superlink/ffs/__init__.py +24 -0
  233. flwr/server/superlink/ffs/disk_ffs.py +108 -0
  234. flwr/server/superlink/ffs/ffs.py +79 -0
  235. flwr/server/superlink/ffs/ffs_factory.py +47 -0
  236. flwr/server/superlink/fleet/__init__.py +1 -1
  237. flwr/server/superlink/fleet/grpc_adapter/__init__.py +15 -0
  238. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +162 -0
  239. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  240. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +4 -2
  241. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +3 -2
  242. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  243. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +5 -154
  244. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  245. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +120 -13
  246. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +228 -0
  247. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  248. flwr/server/superlink/fleet/message_handler/message_handler.py +153 -9
  249. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  250. flwr/server/superlink/fleet/rest_rere/rest_api.py +119 -81
  251. flwr/server/superlink/fleet/vce/__init__.py +1 -0
  252. flwr/server/superlink/fleet/vce/backend/__init__.py +4 -4
  253. flwr/server/superlink/fleet/vce/backend/backend.py +8 -9
  254. flwr/server/superlink/fleet/vce/backend/raybackend.py +87 -68
  255. flwr/server/superlink/fleet/vce/vce_api.py +208 -146
  256. flwr/server/superlink/linkstate/__init__.py +28 -0
  257. flwr/server/superlink/linkstate/in_memory_linkstate.py +581 -0
  258. flwr/server/superlink/linkstate/linkstate.py +389 -0
  259. flwr/server/superlink/{state/state_factory.py → linkstate/linkstate_factory.py} +19 -10
  260. flwr/server/superlink/linkstate/sqlite_linkstate.py +1236 -0
  261. flwr/server/superlink/linkstate/utils.py +389 -0
  262. flwr/server/superlink/simulation/__init__.py +15 -0
  263. flwr/server/superlink/simulation/simulationio_grpc.py +65 -0
  264. flwr/server/superlink/simulation/simulationio_servicer.py +186 -0
  265. flwr/server/superlink/utils.py +65 -0
  266. flwr/server/typing.py +2 -0
  267. flwr/server/utils/__init__.py +1 -1
  268. flwr/server/utils/tensorboard.py +5 -5
  269. flwr/server/utils/validator.py +31 -11
  270. flwr/server/workflow/default_workflows.py +70 -26
  271. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -0
  272. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +40 -27
  273. flwr/simulation/__init__.py +12 -5
  274. flwr/simulation/app.py +247 -315
  275. flwr/simulation/legacy_app.py +402 -0
  276. flwr/simulation/ray_transport/__init__.py +1 -1
  277. flwr/simulation/ray_transport/ray_actor.py +42 -67
  278. flwr/simulation/ray_transport/ray_client_proxy.py +37 -17
  279. flwr/simulation/ray_transport/utils.py +1 -0
  280. flwr/simulation/run_simulation.py +306 -163
  281. flwr/simulation/simulationio_connection.py +89 -0
  282. flwr/superexec/__init__.py +15 -0
  283. flwr/superexec/app.py +59 -0
  284. flwr/superexec/deployment.py +188 -0
  285. flwr/superexec/exec_grpc.py +80 -0
  286. flwr/superexec/exec_servicer.py +231 -0
  287. flwr/superexec/exec_user_auth_interceptor.py +101 -0
  288. flwr/superexec/executor.py +96 -0
  289. flwr/superexec/simulation.py +124 -0
  290. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/METADATA +33 -26
  291. flwr_nightly-1.15.0.dev20250114.dist-info/RECORD +328 -0
  292. flwr_nightly-1.15.0.dev20250114.dist-info/entry_points.txt +12 -0
  293. flwr/cli/flower_toml.py +0 -140
  294. flwr/cli/new/templates/app/flower.toml.tpl +0 -13
  295. flwr/cli/new/templates/app/requirements.numpy.txt.tpl +0 -2
  296. flwr/cli/new/templates/app/requirements.pytorch.txt.tpl +0 -4
  297. flwr/cli/new/templates/app/requirements.tensorflow.txt.tpl +0 -4
  298. flwr/client/node_state.py +0 -48
  299. flwr/client/node_state_tests.py +0 -65
  300. flwr/proto/driver_pb2.py +0 -44
  301. flwr/proto/driver_pb2_grpc.py +0 -169
  302. flwr/proto/driver_pb2_grpc.pyi +0 -66
  303. flwr/server/superlink/driver/driver_grpc.py +0 -54
  304. flwr/server/superlink/driver/driver_servicer.py +0 -129
  305. flwr/server/superlink/state/in_memory_state.py +0 -230
  306. flwr/server/superlink/state/sqlite_state.py +0 -630
  307. flwr/server/superlink/state/state.py +0 -154
  308. flwr_nightly-1.8.0.dev20240315.dist-info/RECORD +0 -211
  309. flwr_nightly-1.8.0.dev20240315.dist-info/entry_points.txt +0 -9
  310. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/LICENSE +0 -0
  311. {flwr_nightly-1.8.0.dev20240315.dist-info → flwr_nightly-1.15.0.dev20250114.dist-info}/WHEEL +0 -0
@@ -0,0 +1,176 @@
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 client interceptor."""
16
+
17
+
18
+ import base64
19
+ import collections
20
+ from collections.abc import Sequence
21
+ from logging import WARNING
22
+ from typing import Any, Callable, Optional, Union
23
+
24
+ import grpc
25
+ from cryptography.hazmat.primitives.asymmetric import ec
26
+
27
+ from flwr.common.logger import log
28
+ from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
29
+ bytes_to_public_key,
30
+ compute_hmac,
31
+ generate_shared_key,
32
+ public_key_to_bytes,
33
+ )
34
+ from flwr.proto.fab_pb2 import GetFabRequest # pylint: disable=E0611
35
+ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
36
+ CreateNodeRequest,
37
+ DeleteNodeRequest,
38
+ PingRequest,
39
+ PullMessagesRequest,
40
+ PullTaskInsRequest,
41
+ PushMessagesRequest,
42
+ PushTaskResRequest,
43
+ )
44
+ from flwr.proto.run_pb2 import GetRunRequest # pylint: disable=E0611
45
+
46
+ _PUBLIC_KEY_HEADER = "public-key"
47
+ _AUTH_TOKEN_HEADER = "auth-token"
48
+
49
+ Request = Union[
50
+ CreateNodeRequest,
51
+ DeleteNodeRequest,
52
+ PullTaskInsRequest,
53
+ PushTaskResRequest,
54
+ GetRunRequest,
55
+ PingRequest,
56
+ GetFabRequest,
57
+ PullMessagesRequest,
58
+ PushMessagesRequest,
59
+ ]
60
+
61
+
62
+ def _get_value_from_tuples(
63
+ key_string: str, tuples: Sequence[tuple[str, Union[str, bytes]]]
64
+ ) -> bytes:
65
+ value = next((value for key, value in tuples if key == key_string), "")
66
+ if isinstance(value, str):
67
+ return value.encode()
68
+
69
+ return value
70
+
71
+
72
+ class _ClientCallDetails(
73
+ collections.namedtuple(
74
+ "_ClientCallDetails", ("method", "timeout", "metadata", "credentials")
75
+ ),
76
+ grpc.ClientCallDetails, # type: ignore
77
+ ):
78
+ """Details for each client call.
79
+
80
+ The class will be passed on as the first argument in continuation function.
81
+ In our case, `AuthenticateClientInterceptor` adds new metadata to the construct.
82
+ """
83
+
84
+
85
+ class AuthenticateClientInterceptor(grpc.UnaryUnaryClientInterceptor): # type: ignore
86
+ """Client interceptor for client authentication."""
87
+
88
+ def __init__(
89
+ self,
90
+ private_key: ec.EllipticCurvePrivateKey,
91
+ public_key: ec.EllipticCurvePublicKey,
92
+ ):
93
+ self.private_key = private_key
94
+ self.public_key = public_key
95
+ self.shared_secret: Optional[bytes] = None
96
+ self.server_public_key: Optional[ec.EllipticCurvePublicKey] = None
97
+ self.encoded_public_key = base64.urlsafe_b64encode(
98
+ public_key_to_bytes(self.public_key)
99
+ )
100
+
101
+ def intercept_unary_unary(
102
+ self,
103
+ continuation: Callable[[Any, Any], Any],
104
+ client_call_details: grpc.ClientCallDetails,
105
+ request: Request,
106
+ ) -> grpc.Call:
107
+ """Flower client interceptor.
108
+
109
+ Intercept unary call from client and add necessary authentication header in the
110
+ RPC metadata.
111
+ """
112
+ metadata = []
113
+ postprocess = False
114
+ if client_call_details.metadata is not None:
115
+ metadata = list(client_call_details.metadata)
116
+
117
+ # Always add the public key header
118
+ metadata.append(
119
+ (
120
+ _PUBLIC_KEY_HEADER,
121
+ self.encoded_public_key,
122
+ )
123
+ )
124
+
125
+ if isinstance(request, CreateNodeRequest):
126
+ postprocess = True
127
+ elif isinstance(
128
+ request,
129
+ (
130
+ DeleteNodeRequest,
131
+ PullTaskInsRequest,
132
+ PushTaskResRequest,
133
+ GetRunRequest,
134
+ PingRequest,
135
+ GetFabRequest,
136
+ PullMessagesRequest,
137
+ PushMessagesRequest,
138
+ ),
139
+ ):
140
+ if self.shared_secret is None:
141
+ raise RuntimeError("Failure to compute hmac")
142
+
143
+ message_bytes = request.SerializeToString(deterministic=True)
144
+ metadata.append(
145
+ (
146
+ _AUTH_TOKEN_HEADER,
147
+ base64.urlsafe_b64encode(
148
+ compute_hmac(self.shared_secret, message_bytes)
149
+ ),
150
+ )
151
+ )
152
+
153
+ client_call_details = _ClientCallDetails(
154
+ client_call_details.method,
155
+ client_call_details.timeout,
156
+ metadata,
157
+ client_call_details.credentials,
158
+ )
159
+
160
+ response = continuation(client_call_details, request)
161
+ if postprocess:
162
+ server_public_key_bytes = base64.urlsafe_b64decode(
163
+ _get_value_from_tuples(_PUBLIC_KEY_HEADER, response.initial_metadata())
164
+ )
165
+
166
+ if server_public_key_bytes != b"":
167
+ self.server_public_key = bytes_to_public_key(server_public_key_bytes)
168
+ else:
169
+ log(WARNING, "Can't get server public key, SuperLink may be offline")
170
+
171
+ if self.server_public_key is not None:
172
+ self.shared_secret = generate_shared_key(
173
+ self.private_key, self.server_public_key
174
+ )
175
+
176
+ return response
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2023 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,32 +15,49 @@
15
15
  """Contextmanager for a gRPC request-response channel to the Flower server."""
16
16
 
17
17
 
18
+ import random
19
+ import threading
20
+ from collections.abc import Iterator, Sequence
18
21
  from contextlib import contextmanager
19
22
  from copy import copy
20
23
  from logging import DEBUG, ERROR
21
24
  from pathlib import Path
22
- from typing import Callable, Dict, Iterator, Optional, Tuple, Union, cast
25
+ from typing import Callable, Optional, Union, cast
23
26
 
27
+ import grpc
28
+ from cryptography.hazmat.primitives.asymmetric import ec
29
+
30
+ from flwr.client.heartbeat import start_ping_loop
24
31
  from flwr.client.message_handler.message_handler import validate_out_message
25
- from flwr.client.message_handler.task_handler import get_task_ins, validate_task_ins
26
32
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH
33
+ from flwr.common.constant import (
34
+ PING_BASE_MULTIPLIER,
35
+ PING_CALL_TIMEOUT,
36
+ PING_DEFAULT_INTERVAL,
37
+ PING_RANDOM_RANGE,
38
+ )
27
39
  from flwr.common.grpc import create_channel
28
- from flwr.common.logger import log, warn_experimental_feature
40
+ from flwr.common.logger import log
29
41
  from flwr.common.message import Message, Metadata
30
42
  from flwr.common.retry_invoker import RetryInvoker
31
- from flwr.common.serde import message_from_taskins, message_to_taskres
43
+ from flwr.common.serde import message_from_proto, message_to_proto, run_from_proto
44
+ from flwr.common.typing import Fab, Run, RunNotRunningException
45
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
32
46
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
33
47
  CreateNodeRequest,
34
48
  DeleteNodeRequest,
35
- PullTaskInsRequest,
36
- PushTaskResRequest,
49
+ PingRequest,
50
+ PingResponse,
51
+ PullMessagesRequest,
52
+ PullMessagesResponse,
53
+ PushMessagesRequest,
37
54
  )
38
55
  from flwr.proto.fleet_pb2_grpc import FleetStub # pylint: disable=E0611
39
56
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
40
- from flwr.proto.task_pb2 import TaskIns # pylint: disable=E0611
57
+ from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
41
58
 
42
- KEY_NODE = "node"
43
- KEY_METADATA = "in_message_metadata"
59
+ from .client_interceptor import AuthenticateClientInterceptor
60
+ from .grpc_adapter import GrpcAdapter
44
61
 
45
62
 
46
63
  def on_channel_state_change(channel_connectivity: str) -> None:
@@ -49,18 +66,24 @@ def on_channel_state_change(channel_connectivity: str) -> None:
49
66
 
50
67
 
51
68
  @contextmanager
52
- def grpc_request_response(
69
+ def grpc_request_response( # pylint: disable=R0913,R0914,R0915,R0917
53
70
  server_address: str,
54
71
  insecure: bool,
55
72
  retry_invoker: RetryInvoker,
56
73
  max_message_length: int = GRPC_MAX_MESSAGE_LENGTH, # pylint: disable=W0613
57
74
  root_certificates: Optional[Union[bytes, str]] = None,
75
+ authentication_keys: Optional[
76
+ tuple[ec.EllipticCurvePrivateKey, ec.EllipticCurvePublicKey]
77
+ ] = None,
78
+ adapter_cls: Optional[Union[type[FleetStub], type[GrpcAdapter]]] = None,
58
79
  ) -> Iterator[
59
- Tuple[
80
+ tuple[
60
81
  Callable[[], Optional[Message]],
61
82
  Callable[[Message], None],
83
+ Optional[Callable[[], Optional[int]]],
62
84
  Optional[Callable[[], None]],
63
- Optional[Callable[[], None]],
85
+ Optional[Callable[[int], Run]],
86
+ Optional[Callable[[str, int], Fab]],
64
87
  ]
65
88
  ]:
66
89
  """Primitives for request/response-based interaction with a server.
@@ -87,6 +110,14 @@ def grpc_request_response(
87
110
  Path of the root certificate. If provided, a secure
88
111
  connection using the certificates will be established to an SSL-enabled
89
112
  Flower server. Bytes won't work for the REST API.
113
+ authentication_keys : Optional[Tuple[PrivateKey, PublicKey]] (default: None)
114
+ Tuple containing the elliptic curve private key and public key for
115
+ authentication from the cryptography library.
116
+ Source: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/ec/
117
+ Used to establish an authenticated connection with the server.
118
+ adapter_cls: Optional[Union[type[FleetStub], type[GrpcAdapter]]] (default: None)
119
+ A GrpcStub Class that can be used to send messages. By default the FleetStub
120
+ will be used.
90
121
 
91
122
  Returns
92
123
  -------
@@ -94,113 +125,189 @@ def grpc_request_response(
94
125
  send : Callable
95
126
  create_node : Optional[Callable]
96
127
  delete_node : Optional[Callable]
128
+ get_run : Optional[Callable]
97
129
  """
98
- warn_experimental_feature("`grpc-rere`")
99
-
100
130
  if isinstance(root_certificates, str):
101
131
  root_certificates = Path(root_certificates).read_bytes()
102
132
 
133
+ interceptors: Optional[Sequence[grpc.UnaryUnaryClientInterceptor]] = None
134
+ if authentication_keys is not None:
135
+ interceptors = AuthenticateClientInterceptor(
136
+ authentication_keys[0], authentication_keys[1]
137
+ )
138
+
103
139
  channel = create_channel(
104
140
  server_address=server_address,
105
141
  insecure=insecure,
106
142
  root_certificates=root_certificates,
107
143
  max_message_length=max_message_length,
144
+ interceptors=interceptors,
108
145
  )
109
146
  channel.subscribe(on_channel_state_change)
110
- stub = FleetStub(channel)
111
147
 
112
- # Necessary state to validate messages to be sent
113
- state: Dict[str, Optional[Metadata]] = {KEY_METADATA: None}
114
-
115
- # Enable create_node and delete_node to store node
116
- node_store: Dict[str, Optional[Node]] = {KEY_NODE: None}
148
+ # Shared variables for inner functions
149
+ if adapter_cls is None:
150
+ adapter_cls = FleetStub
151
+ stub = adapter_cls(channel)
152
+ metadata: Optional[Metadata] = None
153
+ node: Optional[Node] = None
154
+ ping_thread: Optional[threading.Thread] = None
155
+ ping_stop_event = threading.Event()
156
+
157
+ def _should_giveup_fn(e: Exception) -> bool:
158
+ if e.code() == grpc.StatusCode.PERMISSION_DENIED: # type: ignore
159
+ raise RunNotRunningException
160
+ if e.code() == grpc.StatusCode.UNAVAILABLE: # type: ignore
161
+ return False
162
+ return True
163
+
164
+ # Restrict retries to cases where the status code is UNAVAILABLE
165
+ # If the status code is PERMISSION_DENIED, additionally raise RunNotRunningException
166
+ retry_invoker.should_giveup = _should_giveup_fn
117
167
 
118
168
  ###########################################################################
119
- # receive/send functions
169
+ # ping/create_node/delete_node/receive/send/get_run functions
120
170
  ###########################################################################
121
171
 
122
- def create_node() -> None:
172
+ def ping() -> None:
173
+ # Get Node
174
+ if node is None:
175
+ log(ERROR, "Node instance missing")
176
+ return
177
+
178
+ # Construct the ping request
179
+ req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
180
+
181
+ # Call FleetAPI
182
+ res: PingResponse = stub.Ping(req, timeout=PING_CALL_TIMEOUT)
183
+
184
+ # Check if success
185
+ if not res.success:
186
+ raise RuntimeError("Ping failed unexpectedly.")
187
+
188
+ # Wait
189
+ rd = random.uniform(*PING_RANDOM_RANGE)
190
+ next_interval: float = PING_DEFAULT_INTERVAL - PING_CALL_TIMEOUT
191
+ next_interval *= PING_BASE_MULTIPLIER + rd
192
+ if not ping_stop_event.is_set():
193
+ ping_stop_event.wait(next_interval)
194
+
195
+ def create_node() -> Optional[int]:
123
196
  """Set create_node."""
124
- create_node_request = CreateNodeRequest()
197
+ # Call FleetAPI
198
+ create_node_request = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
125
199
  create_node_response = retry_invoker.invoke(
126
200
  stub.CreateNode,
127
201
  request=create_node_request,
128
202
  )
129
- node_store[KEY_NODE] = create_node_response.node
203
+
204
+ # Remember the node and the ping-loop thread
205
+ nonlocal node, ping_thread
206
+ node = cast(Node, create_node_response.node)
207
+ ping_thread = start_ping_loop(ping, ping_stop_event)
208
+ return node.node_id
130
209
 
131
210
  def delete_node() -> None:
132
211
  """Set delete_node."""
133
212
  # Get Node
134
- if node_store[KEY_NODE] is None:
213
+ nonlocal node
214
+ if node is None:
135
215
  log(ERROR, "Node instance missing")
136
216
  return
137
- node: Node = cast(Node, node_store[KEY_NODE])
138
217
 
218
+ # Stop the ping-loop thread
219
+ ping_stop_event.set()
220
+
221
+ # Call FleetAPI
139
222
  delete_node_request = DeleteNodeRequest(node=node)
140
223
  retry_invoker.invoke(stub.DeleteNode, request=delete_node_request)
141
224
 
142
- del node_store[KEY_NODE]
225
+ # Cleanup
226
+ node = None
143
227
 
144
228
  def receive() -> Optional[Message]:
145
- """Receive next task from server."""
229
+ """Receive next message from server."""
146
230
  # Get Node
147
- if node_store[KEY_NODE] is None:
231
+ if node is None:
148
232
  log(ERROR, "Node instance missing")
149
233
  return None
150
- node: Node = cast(Node, node_store[KEY_NODE])
151
234
 
152
- # Request instructions (task) from server
153
- request = PullTaskInsRequest(node=node)
154
- response = retry_invoker.invoke(stub.PullTaskIns, request=request)
235
+ # Request instructions (message) from server
236
+ request = PullMessagesRequest(node=node)
237
+ response: PullMessagesResponse = retry_invoker.invoke(
238
+ stub.PullMessages, request=request
239
+ )
155
240
 
156
- # Get the current TaskIns
157
- task_ins: Optional[TaskIns] = get_task_ins(response)
241
+ # Get the current Messages
242
+ message_proto = (
243
+ None if len(response.messages_list) == 0 else response.messages_list[0]
244
+ )
158
245
 
159
- # Discard the current TaskIns if not valid
160
- if task_ins is not None and not (
161
- task_ins.task.consumer.node_id == node.node_id
162
- and validate_task_ins(task_ins)
246
+ # Discard the current message if not valid
247
+ if message_proto is not None and not (
248
+ message_proto.metadata.dst_node_id == node.node_id
163
249
  ):
164
- task_ins = None
250
+ message_proto = None
165
251
 
166
252
  # Construct the Message
167
- in_message = message_from_taskins(task_ins) if task_ins else None
253
+ in_message = message_from_proto(message_proto) if message_proto else None
168
254
 
169
255
  # Remember `metadata` of the in message
170
- state[KEY_METADATA] = copy(in_message.metadata) if in_message else None
256
+ nonlocal metadata
257
+ metadata = copy(in_message.metadata) if in_message else None
171
258
 
172
259
  # Return the message if available
173
260
  return in_message
174
261
 
175
262
  def send(message: Message) -> None:
176
- """Send task result back to server."""
263
+ """Send message reply to server."""
177
264
  # Get Node
178
- if node_store[KEY_NODE] is None:
265
+ if node is None:
179
266
  log(ERROR, "Node instance missing")
180
267
  return
181
268
 
182
- # Get incoming message
183
- in_metadata = state[KEY_METADATA]
184
- if in_metadata is None:
269
+ # Get the metadata of the incoming message
270
+ nonlocal metadata
271
+ if metadata is None:
185
272
  log(ERROR, "No current message")
186
273
  return
187
274
 
188
275
  # Validate out message
189
- if not validate_out_message(message, in_metadata):
276
+ if not validate_out_message(message, metadata):
190
277
  log(ERROR, "Invalid out message")
191
278
  return
192
279
 
193
- # Construct TaskRes
194
- task_res = message_to_taskres(message)
280
+ # Serialize Message
281
+ message_proto = message_to_proto(message=message)
282
+ request = PushMessagesRequest(node=node, messages_list=[message_proto])
283
+ _ = retry_invoker.invoke(stub.PushMessages, request)
284
+
285
+ # Cleanup
286
+ metadata = None
287
+
288
+ def get_run(run_id: int) -> Run:
289
+ # Call FleetAPI
290
+ get_run_request = GetRunRequest(node=node, run_id=run_id)
291
+ get_run_response: GetRunResponse = retry_invoker.invoke(
292
+ stub.GetRun,
293
+ request=get_run_request,
294
+ )
295
+
296
+ # Return fab_id and fab_version
297
+ return run_from_proto(get_run_response.run)
195
298
 
196
- # Serialize ProtoBuf to bytes
197
- request = PushTaskResRequest(task_res_list=[task_res])
198
- _ = retry_invoker.invoke(stub.PushTaskRes, request)
299
+ def get_fab(fab_hash: str, run_id: int) -> Fab:
300
+ # Call FleetAPI
301
+ get_fab_request = GetFabRequest(node=node, hash_str=fab_hash, run_id=run_id)
302
+ get_fab_response: GetFabResponse = retry_invoker.invoke(
303
+ stub.GetFab,
304
+ request=get_fab_request,
305
+ )
199
306
 
200
- state[KEY_METADATA] = None
307
+ return Fab(get_fab_response.fab.hash_str, get_fab_response.fab.content)
201
308
 
202
309
  try:
203
310
  # Yield methods
204
- yield (receive, send, create_node, delete_node)
311
+ yield (receive, send, create_node, delete_node, get_run, get_fab)
205
312
  except Exception as exc: # pylint: disable=broad-except
206
313
  log(ERROR, exc)
@@ -0,0 +1,167 @@
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
+ """GrpcAdapter implementation."""
16
+
17
+
18
+ import sys
19
+ from logging import DEBUG
20
+ from typing import Any, TypeVar, cast
21
+
22
+ import grpc
23
+ from google.protobuf.message import Message as GrpcMessage
24
+
25
+ from flwr.common import log
26
+ from flwr.common.constant import (
27
+ GRPC_ADAPTER_METADATA_FLOWER_PACKAGE_NAME_KEY,
28
+ GRPC_ADAPTER_METADATA_FLOWER_PACKAGE_VERSION_KEY,
29
+ GRPC_ADAPTER_METADATA_FLOWER_VERSION_KEY,
30
+ GRPC_ADAPTER_METADATA_MESSAGE_MODULE_KEY,
31
+ GRPC_ADAPTER_METADATA_MESSAGE_QUALNAME_KEY,
32
+ GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY,
33
+ )
34
+ from flwr.common.version import package_name, package_version
35
+ from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
36
+ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
37
+ CreateNodeRequest,
38
+ CreateNodeResponse,
39
+ DeleteNodeRequest,
40
+ DeleteNodeResponse,
41
+ PingRequest,
42
+ PingResponse,
43
+ PullMessagesRequest,
44
+ PullMessagesResponse,
45
+ PullTaskInsRequest,
46
+ PullTaskInsResponse,
47
+ PushMessagesRequest,
48
+ PushMessagesResponse,
49
+ PushTaskResRequest,
50
+ PushTaskResResponse,
51
+ )
52
+ from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
53
+ from flwr.proto.grpcadapter_pb2_grpc import GrpcAdapterStub
54
+ from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
55
+
56
+ T = TypeVar("T", bound=GrpcMessage)
57
+
58
+
59
+ class GrpcAdapter:
60
+ """Adapter class to send and receive gRPC messages via the ``GrpcAdapterStub``.
61
+
62
+ This class utilizes the ``GrpcAdapterStub`` to send and receive gRPC messages
63
+ which are defined and used by the Fleet API, as defined in ``fleet.proto``.
64
+ """
65
+
66
+ def __init__(self, channel: grpc.Channel) -> None:
67
+ self.stub = GrpcAdapterStub(channel)
68
+
69
+ def _send_and_receive(
70
+ self, request: GrpcMessage, response_type: type[T], **kwargs: Any
71
+ ) -> T:
72
+ # Serialize request
73
+ req_cls = request.__class__
74
+ container_req = MessageContainer(
75
+ metadata={
76
+ GRPC_ADAPTER_METADATA_FLOWER_PACKAGE_NAME_KEY: package_name,
77
+ GRPC_ADAPTER_METADATA_FLOWER_PACKAGE_VERSION_KEY: package_version,
78
+ GRPC_ADAPTER_METADATA_FLOWER_VERSION_KEY: package_version,
79
+ GRPC_ADAPTER_METADATA_MESSAGE_MODULE_KEY: req_cls.__module__,
80
+ GRPC_ADAPTER_METADATA_MESSAGE_QUALNAME_KEY: req_cls.__qualname__,
81
+ },
82
+ grpc_message_name=req_cls.__qualname__,
83
+ grpc_message_content=request.SerializeToString(),
84
+ )
85
+
86
+ # Send via the stub
87
+ container_res = cast(
88
+ MessageContainer, self.stub.SendReceive(container_req, **kwargs)
89
+ )
90
+
91
+ # Handle control message
92
+ should_exit = (
93
+ container_res.metadata.get(GRPC_ADAPTER_METADATA_SHOULD_EXIT_KEY, "false")
94
+ == "true"
95
+ )
96
+ if should_exit:
97
+ log(
98
+ DEBUG,
99
+ 'Received shutdown signal: exit flag is set to ``"true"``. Exiting...',
100
+ )
101
+ sys.exit(0)
102
+
103
+ # Check the grpc_message_name of the response
104
+ if container_res.grpc_message_name != response_type.__qualname__:
105
+ raise ValueError(
106
+ f"Invalid grpc_message_name. Expected {response_type.__qualname__}"
107
+ f", but got {container_res.grpc_message_name}."
108
+ )
109
+
110
+ # Deserialize response
111
+ response = response_type()
112
+ response.ParseFromString(container_res.grpc_message_content)
113
+ return response
114
+
115
+ def CreateNode( # pylint: disable=C0103
116
+ self, request: CreateNodeRequest, **kwargs: Any
117
+ ) -> CreateNodeResponse:
118
+ """."""
119
+ return self._send_and_receive(request, CreateNodeResponse, **kwargs)
120
+
121
+ def DeleteNode( # pylint: disable=C0103
122
+ self, request: DeleteNodeRequest, **kwargs: Any
123
+ ) -> DeleteNodeResponse:
124
+ """."""
125
+ return self._send_and_receive(request, DeleteNodeResponse, **kwargs)
126
+
127
+ def Ping( # pylint: disable=C0103
128
+ self, request: PingRequest, **kwargs: Any
129
+ ) -> PingResponse:
130
+ """."""
131
+ return self._send_and_receive(request, PingResponse, **kwargs)
132
+
133
+ def PullTaskIns( # pylint: disable=C0103
134
+ self, request: PullTaskInsRequest, **kwargs: Any
135
+ ) -> PullTaskInsResponse:
136
+ """."""
137
+ return self._send_and_receive(request, PullTaskInsResponse, **kwargs)
138
+
139
+ def PullMessages( # pylint: disable=C0103
140
+ self, request: PullMessagesRequest, **kwargs: Any
141
+ ) -> PullMessagesResponse:
142
+ """."""
143
+ return self._send_and_receive(request, PullMessagesResponse, **kwargs)
144
+
145
+ def PushTaskRes( # pylint: disable=C0103
146
+ self, request: PushTaskResRequest, **kwargs: Any
147
+ ) -> PushTaskResResponse:
148
+ """."""
149
+ return self._send_and_receive(request, PushTaskResResponse, **kwargs)
150
+
151
+ def PushMessages( # pylint: disable=C0103
152
+ self, request: PushMessagesRequest, **kwargs: Any
153
+ ) -> PushMessagesResponse:
154
+ """."""
155
+ return self._send_and_receive(request, PushMessagesResponse, **kwargs)
156
+
157
+ def GetRun( # pylint: disable=C0103
158
+ self, request: GetRunRequest, **kwargs: Any
159
+ ) -> GetRunResponse:
160
+ """."""
161
+ return self._send_and_receive(request, GetRunResponse, **kwargs)
162
+
163
+ def GetFab( # pylint: disable=C0103
164
+ self, request: GetFabRequest, **kwargs: Any
165
+ ) -> GetFabResponse:
166
+ """."""
167
+ return self._send_and_receive(request, GetFabResponse, **kwargs)