flwr 1.17.0__py3-none-any.whl → 1.19.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 (286) hide show
  1. flwr/__init__.py +1 -1
  2. flwr/app/__init__.py +15 -0
  3. flwr/app/error.py +68 -0
  4. flwr/app/metadata.py +223 -0
  5. flwr/cli/__init__.py +1 -1
  6. flwr/cli/app.py +21 -2
  7. flwr/cli/build.py +83 -58
  8. flwr/cli/cli_user_auth_interceptor.py +1 -1
  9. flwr/cli/config_utils.py +53 -17
  10. flwr/cli/example.py +1 -1
  11. flwr/cli/install.py +1 -1
  12. flwr/cli/log.py +4 -4
  13. flwr/cli/login/__init__.py +1 -1
  14. flwr/cli/login/login.py +15 -8
  15. flwr/cli/ls.py +16 -37
  16. flwr/cli/new/__init__.py +1 -1
  17. flwr/cli/new/new.py +4 -4
  18. flwr/cli/new/templates/__init__.py +1 -1
  19. flwr/cli/new/templates/app/__init__.py +1 -1
  20. flwr/cli/new/templates/app/code/__init__.py +1 -1
  21. flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
  22. flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
  23. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
  24. flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
  25. flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
  26. flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
  27. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
  28. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
  29. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
  30. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  31. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
  32. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  33. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
  34. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  35. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  36. flwr/cli/run/__init__.py +1 -1
  37. flwr/cli/run/run.py +11 -19
  38. flwr/cli/stop.py +3 -3
  39. flwr/cli/utils.py +42 -17
  40. flwr/client/__init__.py +3 -3
  41. flwr/client/client.py +1 -1
  42. flwr/client/client_app.py +140 -138
  43. flwr/client/clientapp/__init__.py +1 -8
  44. flwr/client/clientapp/utils.py +1 -1
  45. flwr/client/dpfedavg_numpy_client.py +1 -1
  46. flwr/client/grpc_adapter_client/__init__.py +1 -1
  47. flwr/client/grpc_adapter_client/connection.py +5 -5
  48. flwr/client/grpc_rere_client/__init__.py +1 -1
  49. flwr/client/grpc_rere_client/client_interceptor.py +1 -1
  50. flwr/client/grpc_rere_client/connection.py +131 -61
  51. flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
  52. flwr/client/message_handler/__init__.py +1 -1
  53. flwr/client/message_handler/message_handler.py +2 -2
  54. flwr/client/mod/__init__.py +1 -1
  55. flwr/client/mod/centraldp_mods.py +1 -1
  56. flwr/client/mod/comms_mods.py +39 -20
  57. flwr/client/mod/localdp_mod.py +6 -6
  58. flwr/client/mod/secure_aggregation/__init__.py +1 -1
  59. flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
  60. flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
  61. flwr/client/mod/utils.py +1 -1
  62. flwr/client/numpy_client.py +1 -1
  63. flwr/client/rest_client/__init__.py +1 -1
  64. flwr/client/rest_client/connection.py +174 -68
  65. flwr/client/run_info_store.py +1 -1
  66. flwr/client/typing.py +1 -1
  67. flwr/clientapp/__init__.py +15 -0
  68. flwr/common/__init__.py +3 -3
  69. flwr/common/address.py +1 -1
  70. flwr/common/args.py +1 -1
  71. flwr/common/auth_plugin/__init__.py +3 -1
  72. flwr/common/auth_plugin/auth_plugin.py +30 -4
  73. flwr/common/config.py +1 -1
  74. flwr/common/constant.py +37 -8
  75. flwr/common/context.py +1 -1
  76. flwr/common/date.py +1 -1
  77. flwr/common/differential_privacy.py +1 -1
  78. flwr/common/differential_privacy_constants.py +1 -1
  79. flwr/common/dp.py +1 -1
  80. flwr/common/event_log_plugin/event_log_plugin.py +3 -3
  81. flwr/common/exit/exit.py +6 -6
  82. flwr/common/exit_handlers.py +31 -1
  83. flwr/common/grpc.py +1 -1
  84. flwr/common/heartbeat.py +165 -0
  85. flwr/common/inflatable.py +290 -0
  86. flwr/common/inflatable_grpc_utils.py +99 -0
  87. flwr/common/inflatable_rest_utils.py +99 -0
  88. flwr/common/inflatable_utils.py +341 -0
  89. flwr/common/logger.py +1 -1
  90. flwr/common/message.py +137 -252
  91. flwr/common/object_ref.py +1 -1
  92. flwr/common/parameter.py +1 -1
  93. flwr/common/pyproject.py +1 -1
  94. flwr/common/record/__init__.py +3 -2
  95. flwr/common/record/array.py +323 -0
  96. flwr/common/record/arrayrecord.py +121 -243
  97. flwr/common/record/configrecord.py +71 -16
  98. flwr/common/record/conversion_utils.py +2 -2
  99. flwr/common/record/metricrecord.py +71 -20
  100. flwr/common/record/recorddict.py +207 -90
  101. flwr/common/record/typeddict.py +1 -1
  102. flwr/common/recorddict_compat.py +2 -2
  103. flwr/common/retry_invoker.py +15 -11
  104. flwr/common/secure_aggregation/__init__.py +1 -1
  105. flwr/common/secure_aggregation/crypto/__init__.py +1 -1
  106. flwr/common/secure_aggregation/crypto/shamir.py +52 -30
  107. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
  108. flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
  109. flwr/common/secure_aggregation/quantization.py +1 -1
  110. flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
  111. flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
  112. flwr/common/serde.py +60 -184
  113. flwr/common/serde_utils.py +175 -0
  114. flwr/common/telemetry.py +2 -2
  115. flwr/common/typing.py +6 -4
  116. flwr/common/version.py +1 -1
  117. flwr/compat/__init__.py +15 -0
  118. flwr/compat/client/__init__.py +15 -0
  119. flwr/{client → compat/client}/app.py +71 -211
  120. flwr/{client → compat/client}/grpc_client/__init__.py +1 -1
  121. flwr/{client → compat/client}/grpc_client/connection.py +13 -13
  122. flwr/compat/common/__init__.py +15 -0
  123. flwr/compat/server/__init__.py +15 -0
  124. flwr/compat/server/app.py +174 -0
  125. flwr/compat/simulation/__init__.py +15 -0
  126. flwr/proto/__init__.py +1 -1
  127. flwr/proto/fleet_pb2.py +32 -27
  128. flwr/proto/fleet_pb2.pyi +49 -35
  129. flwr/proto/fleet_pb2_grpc.py +117 -13
  130. flwr/proto/fleet_pb2_grpc.pyi +47 -6
  131. flwr/proto/heartbeat_pb2.py +33 -0
  132. flwr/proto/heartbeat_pb2.pyi +66 -0
  133. flwr/proto/heartbeat_pb2_grpc.py +4 -0
  134. flwr/proto/heartbeat_pb2_grpc.pyi +4 -0
  135. flwr/proto/message_pb2.py +28 -11
  136. flwr/proto/message_pb2.pyi +125 -0
  137. flwr/proto/recorddict_pb2.py +16 -28
  138. flwr/proto/recorddict_pb2.pyi +46 -64
  139. flwr/proto/run_pb2.py +24 -32
  140. flwr/proto/run_pb2.pyi +4 -52
  141. flwr/proto/serverappio_pb2.py +32 -23
  142. flwr/proto/serverappio_pb2.pyi +45 -3
  143. flwr/proto/serverappio_pb2_grpc.py +138 -34
  144. flwr/proto/serverappio_pb2_grpc.pyi +54 -13
  145. flwr/proto/simulationio_pb2.py +12 -11
  146. flwr/proto/simulationio_pb2_grpc.py +35 -0
  147. flwr/proto/simulationio_pb2_grpc.pyi +14 -0
  148. flwr/server/__init__.py +2 -2
  149. flwr/server/app.py +69 -187
  150. flwr/server/client_manager.py +1 -1
  151. flwr/server/client_proxy.py +1 -1
  152. flwr/server/compat/__init__.py +1 -1
  153. flwr/server/compat/app.py +1 -1
  154. flwr/server/compat/app_utils.py +51 -29
  155. flwr/server/compat/legacy_context.py +1 -1
  156. flwr/server/criterion.py +1 -1
  157. flwr/server/fleet_event_log_interceptor.py +2 -2
  158. flwr/server/grid/grid.py +3 -3
  159. flwr/server/grid/grpc_grid.py +104 -34
  160. flwr/server/grid/inmemory_grid.py +5 -4
  161. flwr/server/history.py +1 -1
  162. flwr/server/run_serverapp.py +1 -1
  163. flwr/server/server.py +1 -1
  164. flwr/server/server_app.py +65 -58
  165. flwr/server/server_config.py +1 -1
  166. flwr/server/serverapp/__init__.py +1 -1
  167. flwr/server/serverapp/app.py +19 -1
  168. flwr/server/serverapp_components.py +1 -1
  169. flwr/server/strategy/__init__.py +1 -1
  170. flwr/server/strategy/aggregate.py +1 -1
  171. flwr/server/strategy/bulyan.py +2 -2
  172. flwr/server/strategy/dp_adaptive_clipping.py +17 -17
  173. flwr/server/strategy/dp_fixed_clipping.py +17 -17
  174. flwr/server/strategy/dpfedavg_adaptive.py +1 -1
  175. flwr/server/strategy/dpfedavg_fixed.py +1 -1
  176. flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
  177. flwr/server/strategy/fedadagrad.py +1 -1
  178. flwr/server/strategy/fedadam.py +1 -1
  179. flwr/server/strategy/fedavg.py +1 -1
  180. flwr/server/strategy/fedavg_android.py +1 -1
  181. flwr/server/strategy/fedavgm.py +1 -1
  182. flwr/server/strategy/fedmedian.py +1 -1
  183. flwr/server/strategy/fedopt.py +1 -1
  184. flwr/server/strategy/fedprox.py +1 -1
  185. flwr/server/strategy/fedtrimmedavg.py +1 -1
  186. flwr/server/strategy/fedxgb_bagging.py +1 -1
  187. flwr/server/strategy/fedxgb_cyclic.py +1 -1
  188. flwr/server/strategy/fedxgb_nn_avg.py +3 -2
  189. flwr/server/strategy/fedyogi.py +1 -1
  190. flwr/server/strategy/krum.py +1 -1
  191. flwr/server/strategy/qfedavg.py +1 -1
  192. flwr/server/strategy/strategy.py +1 -1
  193. flwr/server/superlink/__init__.py +1 -1
  194. flwr/server/superlink/ffs/__init__.py +3 -1
  195. flwr/server/superlink/ffs/disk_ffs.py +1 -1
  196. flwr/server/superlink/ffs/ffs.py +1 -1
  197. flwr/server/superlink/ffs/ffs_factory.py +1 -1
  198. flwr/server/superlink/fleet/__init__.py +1 -1
  199. flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
  200. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
  201. flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
  202. flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
  203. flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
  204. flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
  205. flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
  206. flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
  207. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
  208. flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
  209. flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
  210. flwr/server/superlink/fleet/message_handler/message_handler.py +136 -19
  211. flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
  212. flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
  213. flwr/server/superlink/fleet/vce/__init__.py +1 -1
  214. flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
  215. flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
  216. flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
  217. flwr/server/superlink/fleet/vce/vce_api.py +7 -4
  218. flwr/server/superlink/linkstate/__init__.py +1 -1
  219. flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
  220. flwr/server/superlink/linkstate/linkstate.py +54 -21
  221. flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
  222. flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
  223. flwr/server/superlink/linkstate/utils.py +34 -30
  224. flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
  225. flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
  226. flwr/server/superlink/simulation/__init__.py +1 -1
  227. flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
  228. flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
  229. flwr/server/superlink/utils.py +45 -3
  230. flwr/server/typing.py +1 -1
  231. flwr/server/utils/__init__.py +1 -1
  232. flwr/server/utils/tensorboard.py +1 -1
  233. flwr/server/utils/validator.py +3 -3
  234. flwr/server/workflow/__init__.py +1 -1
  235. flwr/server/workflow/constant.py +1 -1
  236. flwr/server/workflow/default_workflows.py +1 -1
  237. flwr/server/workflow/secure_aggregation/__init__.py +1 -1
  238. flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
  239. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
  240. flwr/serverapp/__init__.py +15 -0
  241. flwr/simulation/__init__.py +1 -1
  242. flwr/simulation/app.py +18 -1
  243. flwr/simulation/legacy_app.py +1 -1
  244. flwr/simulation/ray_transport/__init__.py +1 -1
  245. flwr/simulation/ray_transport/ray_actor.py +1 -1
  246. flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
  247. flwr/simulation/ray_transport/utils.py +1 -1
  248. flwr/simulation/run_simulation.py +2 -2
  249. flwr/simulation/simulationio_connection.py +1 -1
  250. flwr/supercore/__init__.py +15 -0
  251. flwr/supercore/object_store/__init__.py +24 -0
  252. flwr/supercore/object_store/in_memory_object_store.py +229 -0
  253. flwr/supercore/object_store/object_store.py +192 -0
  254. flwr/supercore/object_store/object_store_factory.py +44 -0
  255. flwr/superexec/__init__.py +1 -1
  256. flwr/superexec/app.py +1 -1
  257. flwr/superexec/deployment.py +7 -3
  258. flwr/superexec/exec_event_log_interceptor.py +4 -4
  259. flwr/superexec/exec_grpc.py +8 -4
  260. flwr/superexec/exec_servicer.py +126 -24
  261. flwr/superexec/exec_user_auth_interceptor.py +38 -9
  262. flwr/superexec/executor.py +5 -1
  263. flwr/superexec/simulation.py +8 -2
  264. flwr/superlink/__init__.py +15 -0
  265. flwr/{client/supernode → supernode}/__init__.py +1 -8
  266. flwr/{client/nodestate/nodestate.py → supernode/cli/__init__.py} +8 -15
  267. flwr/{client/supernode/app.py → supernode/cli/flower_supernode.py} +4 -13
  268. flwr/supernode/cli/flwr_clientapp.py +81 -0
  269. flwr/{client → supernode}/nodestate/__init__.py +1 -1
  270. flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
  271. flwr/supernode/nodestate/nodestate.py +212 -0
  272. flwr/{client → supernode}/nodestate/nodestate_factory.py +1 -1
  273. flwr/supernode/runtime/__init__.py +15 -0
  274. flwr/{client/clientapp/app.py → supernode/runtime/run_clientapp.py} +26 -57
  275. flwr/supernode/servicer/__init__.py +15 -0
  276. flwr/supernode/servicer/clientappio/__init__.py +24 -0
  277. flwr/{client/clientapp → supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
  278. flwr/supernode/start_client_internal.py +491 -0
  279. {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/METADATA +6 -5
  280. flwr-1.19.0.dist-info/RECORD +365 -0
  281. {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/WHEEL +1 -1
  282. {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/entry_points.txt +2 -2
  283. flwr/client/heartbeat.py +0 -74
  284. flwr/client/nodestate/in_memory_nodestate.py +0 -38
  285. flwr-1.17.0.dist-info/LICENSE +0 -202
  286. flwr-1.17.0.dist-info/RECORD +0 -333
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
@@ -14,33 +14,40 @@
14
14
  # ==============================================================================
15
15
  """Contextmanager for a REST request-response channel to the Flower server."""
16
16
 
17
-
18
- import random
19
- import threading
20
17
  from collections.abc import Iterator
21
18
  from contextlib import contextmanager
22
19
  from copy import copy
23
- from logging import ERROR, INFO, WARN
24
- from typing import Callable, Optional, TypeVar, Union
20
+ from logging import DEBUG, ERROR, INFO, WARN
21
+ from typing import Callable, Optional, TypeVar, Union, cast
25
22
 
26
23
  from cryptography.hazmat.primitives.asymmetric import ec
27
24
  from google.protobuf.message import Message as GrpcMessage
28
25
  from requests.exceptions import ConnectionError as RequestsConnectionError
29
26
 
30
- from flwr.client.heartbeat import start_ping_loop
27
+ from flwr.app.metadata import Metadata
31
28
  from flwr.client.message_handler.message_handler import validate_out_message
32
29
  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
- )
30
+ from flwr.common.constant import HEARTBEAT_DEFAULT_INTERVAL
39
31
  from flwr.common.exit import ExitCode, flwr_exit
32
+ from flwr.common.heartbeat import HeartbeatSender
33
+ from flwr.common.inflatable import (
34
+ get_all_nested_objects,
35
+ get_object_tree,
36
+ no_object_id_recompute,
37
+ )
38
+ from flwr.common.inflatable_rest_utils import (
39
+ make_pull_object_fn_rest,
40
+ make_push_object_fn_rest,
41
+ )
42
+ from flwr.common.inflatable_utils import (
43
+ inflate_object_from_contents,
44
+ pull_objects,
45
+ push_objects,
46
+ )
40
47
  from flwr.common.logger import log
41
- from flwr.common.message import Message, Metadata
48
+ from flwr.common.message import Message, remove_content_from_message
42
49
  from flwr.common.retry_invoker import RetryInvoker
43
- from flwr.common.serde import message_from_proto, message_to_proto, run_from_proto
50
+ from flwr.common.serde import message_to_proto, run_from_proto
44
51
  from flwr.common.typing import Fab, Run
45
52
  from flwr.proto.fab_pb2 import GetFabRequest, GetFabResponse # pylint: disable=E0611
46
53
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
@@ -48,13 +55,23 @@ from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
48
55
  CreateNodeResponse,
49
56
  DeleteNodeRequest,
50
57
  DeleteNodeResponse,
51
- PingRequest,
52
- PingResponse,
53
58
  PullMessagesRequest,
54
59
  PullMessagesResponse,
55
60
  PushMessagesRequest,
56
61
  PushMessagesResponse,
57
62
  )
63
+ from flwr.proto.heartbeat_pb2 import ( # pylint: disable=E0611
64
+ SendNodeHeartbeatRequest,
65
+ SendNodeHeartbeatResponse,
66
+ )
67
+ from flwr.proto.message_pb2 import ( # pylint: disable=E0611
68
+ ConfirmMessageReceivedRequest,
69
+ ConfirmMessageReceivedResponse,
70
+ PullObjectRequest,
71
+ PullObjectResponse,
72
+ PushObjectRequest,
73
+ PushObjectResponse,
74
+ )
58
75
  from flwr.proto.node_pb2 import Node # pylint: disable=E0611
59
76
  from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
60
77
 
@@ -68,9 +85,12 @@ PATH_CREATE_NODE: str = "api/v0/fleet/create-node"
68
85
  PATH_DELETE_NODE: str = "api/v0/fleet/delete-node"
69
86
  PATH_PULL_MESSAGES: str = "/api/v0/fleet/pull-messages"
70
87
  PATH_PUSH_MESSAGES: str = "/api/v0/fleet/push-messages"
71
- PATH_PING: str = "api/v0/fleet/ping"
88
+ PATH_PULL_OBJECT: str = "/api/v0/fleet/pull-object"
89
+ PATH_PUSH_OBJECT: str = "/api/v0/fleet/push-object"
90
+ PATH_SEND_NODE_HEARTBEAT: str = "api/v0/fleet/send-node-heartbeat"
72
91
  PATH_GET_RUN: str = "/api/v0/fleet/get-run"
73
92
  PATH_GET_FAB: str = "/api/v0/fleet/get-fab"
93
+ PATH_CONFIRM_MESSAGE_RECEIVED: str = "/api/v0/fleet/confirm-message-received"
74
94
 
75
95
  T = TypeVar("T", bound=GrpcMessage)
76
96
 
@@ -91,10 +111,10 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
91
111
  tuple[
92
112
  Callable[[], Optional[Message]],
93
113
  Callable[[Message], None],
94
- Optional[Callable[[], Optional[int]]],
95
- Optional[Callable[[], None]],
96
- Optional[Callable[[int], Run]],
97
- Optional[Callable[[str, int], Fab]],
114
+ Callable[[], Optional[int]],
115
+ Callable[[], None],
116
+ Callable[[int], Run],
117
+ Callable[[str, int], Fab],
98
118
  ]
99
119
  ]:
100
120
  """Primitives for request/response-based interaction with a server.
@@ -160,11 +180,9 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
160
180
  # Shared variables for inner functions
161
181
  metadata: Optional[Metadata] = None
162
182
  node: Optional[Node] = None
163
- ping_thread: Optional[threading.Thread] = None
164
- ping_stop_event = threading.Event()
165
183
 
166
184
  ###########################################################################
167
- # ping/create_node/delete_node/receive/send/get_run functions
185
+ # heartbeat/create_node/delete_node/receive/send/get_run functions
168
186
  ###########################################################################
169
187
 
170
188
  def _request(
@@ -214,44 +232,47 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
214
232
  grpc_res.ParseFromString(res.content)
215
233
  return grpc_res
216
234
 
217
- def ping() -> None:
235
+ def send_node_heartbeat() -> bool:
218
236
  # Get Node
219
237
  if node is None:
220
238
  log(ERROR, "Node instance missing")
221
- return
239
+ return False
222
240
 
223
- # Construct the ping request
224
- req = PingRequest(node=node, ping_interval=PING_DEFAULT_INTERVAL)
241
+ # Construct the heartbeat request
242
+ req = SendNodeHeartbeatRequest(
243
+ node=node, heartbeat_interval=HEARTBEAT_DEFAULT_INTERVAL
244
+ )
225
245
 
226
246
  # Send the request
227
- res = _request(req, PingResponse, PATH_PING, retry=False)
247
+ res = _request(
248
+ req, SendNodeHeartbeatResponse, PATH_SEND_NODE_HEARTBEAT, retry=False
249
+ )
228
250
  if res is None:
229
- return
251
+ return False
230
252
 
231
253
  # Check if success
232
254
  if not res.success:
233
- raise RuntimeError("Ping failed unexpectedly.")
255
+ raise RuntimeError(
256
+ "Heartbeat failed unexpectedly. The SuperLink does not "
257
+ "recognize this SuperNode."
258
+ )
259
+ return True
234
260
 
235
- # Wait
236
- rd = random.uniform(*PING_RANDOM_RANGE)
237
- next_interval: float = PING_DEFAULT_INTERVAL - PING_CALL_TIMEOUT
238
- next_interval *= PING_BASE_MULTIPLIER + rd
239
- if not ping_stop_event.is_set():
240
- ping_stop_event.wait(next_interval)
261
+ heartbeat_sender = HeartbeatSender(send_node_heartbeat)
241
262
 
242
263
  def create_node() -> Optional[int]:
243
264
  """Set create_node."""
244
- req = CreateNodeRequest(ping_interval=PING_DEFAULT_INTERVAL)
265
+ req = CreateNodeRequest(heartbeat_interval=HEARTBEAT_DEFAULT_INTERVAL)
245
266
 
246
267
  # Send the request
247
268
  res = _request(req, CreateNodeResponse, PATH_CREATE_NODE)
248
269
  if res is None:
249
270
  return None
250
271
 
251
- # Remember the node and the ping-loop thread
252
- nonlocal node, ping_thread
272
+ # Remember the node and start the heartbeat sender
273
+ nonlocal node
253
274
  node = res.node
254
- ping_thread = start_ping_loop(ping, ping_stop_event)
275
+ heartbeat_sender.start()
255
276
  return node.node_id
256
277
 
257
278
  def delete_node() -> None:
@@ -261,10 +282,8 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
261
282
  log(ERROR, "Node instance missing")
262
283
  return
263
284
 
264
- # Stop the ping-loop thread
265
- ping_stop_event.set()
266
- if ping_thread is not None:
267
- ping_thread.join()
285
+ # Stop the heartbeat sender
286
+ heartbeat_sender.stop()
268
287
 
269
288
  # Send DeleteNode request
270
289
  req = DeleteNodeRequest(node=node)
@@ -301,14 +320,58 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
301
320
  ):
302
321
  message_proto = None
303
322
 
304
- # Return the Message if available
305
- nonlocal metadata
306
- message = None
307
- if message_proto is not None:
308
- message = message_from_proto(message_proto)
309
- metadata = copy(message.metadata)
323
+ # Construct the Message
324
+ in_message: Optional[Message] = None
325
+
326
+ if message_proto:
310
327
  log(INFO, "[Node] POST /%s: success", PATH_PULL_MESSAGES)
311
- return message
328
+ msg_id = message_proto.metadata.message_id
329
+ run_id = message_proto.metadata.run_id
330
+
331
+ def fn(request: PullObjectRequest) -> PullObjectResponse:
332
+ res = _request(
333
+ req=request, res_type=PullObjectResponse, api_path=PATH_PULL_OBJECT
334
+ )
335
+ if res is None:
336
+ raise ValueError("PushObjectResponse is None.")
337
+ return res
338
+
339
+ try:
340
+ all_object_contents = pull_objects(
341
+ list(res.objects_to_pull[msg_id].object_ids) + [msg_id],
342
+ pull_object_fn=make_pull_object_fn_rest(
343
+ pull_object_rest=fn,
344
+ node=node,
345
+ run_id=run_id,
346
+ ),
347
+ )
348
+
349
+ # Confirm that the message has been received
350
+ _request(
351
+ req=ConfirmMessageReceivedRequest(
352
+ node=node, run_id=run_id, message_object_id=msg_id
353
+ ),
354
+ res_type=ConfirmMessageReceivedResponse,
355
+ api_path=PATH_CONFIRM_MESSAGE_RECEIVED,
356
+ )
357
+ except ValueError as e:
358
+ log(
359
+ ERROR,
360
+ "Pulling objects failed. Potential irrecoverable error: %s",
361
+ str(e),
362
+ )
363
+ in_message = cast(
364
+ Message, inflate_object_from_contents(msg_id, all_object_contents)
365
+ )
366
+ # The deflated message doesn't contain the message_id (its own object_id)
367
+ # Inject
368
+ in_message.metadata.__dict__["_message_id"] = msg_id
369
+
370
+ # Remember `metadata` of the in message
371
+ nonlocal metadata
372
+ metadata = copy(in_message.metadata) if in_message else None
373
+
374
+ return in_message
312
375
 
313
376
  def send(message: Message) -> None:
314
377
  """Send Message result back to server."""
@@ -323,29 +386,72 @@ def http_request_response( # pylint: disable=R0913,R0914,R0915,R0917
323
386
  log(ERROR, "No current message")
324
387
  return
325
388
 
389
+ # Set message_id
390
+ message.metadata.__dict__["_message_id"] = message.object_id
326
391
  # Validate out message
327
392
  if not validate_out_message(message, metadata):
328
393
  log(ERROR, "Invalid out message")
329
394
  return
330
- metadata = None
331
395
 
332
- # Serialize ProtoBuf to bytes
333
- message_proto = message_to_proto(message=message)
396
+ with no_object_id_recompute():
397
+ # Get all nested objects
398
+ all_objects = get_all_nested_objects(message)
399
+ object_tree = get_object_tree(message)
334
400
 
335
- # Serialize ProtoBuf to bytes
336
- req = PushMessagesRequest(node=node, messages_list=[message_proto])
401
+ # Serialize Message
402
+ message_proto = message_to_proto(
403
+ message=remove_content_from_message(message)
404
+ )
405
+ req = PushMessagesRequest(
406
+ node=node,
407
+ messages_list=[message_proto],
408
+ message_object_trees=[object_tree],
409
+ )
337
410
 
338
- # Send the request
339
- res = _request(req, PushMessagesResponse, PATH_PUSH_MESSAGES)
340
- if res is None:
341
- return
411
+ # Send the request
412
+ res = _request(req, PushMessagesResponse, PATH_PUSH_MESSAGES)
413
+ if res:
414
+ log(
415
+ INFO,
416
+ "[Node] POST /%s: success, created result %s",
417
+ PATH_PUSH_MESSAGES,
418
+ res.results, # pylint: disable=no-member
419
+ )
420
+
421
+ if res and res.objects_to_push:
422
+ objs_to_push = set(res.objects_to_push[message.object_id].object_ids)
423
+
424
+ def fn(request: PushObjectRequest) -> PushObjectResponse:
425
+ res = _request(
426
+ req=request,
427
+ res_type=PushObjectResponse,
428
+ api_path=PATH_PUSH_OBJECT,
429
+ )
430
+ if res is None:
431
+ raise ValueError("PushObjectResponse is None.")
432
+ return res
433
+
434
+ try:
435
+ push_objects(
436
+ all_objects,
437
+ push_object_fn=make_push_object_fn_rest(
438
+ push_object_rest=fn,
439
+ node=node,
440
+ run_id=message_proto.metadata.run_id,
441
+ ),
442
+ object_ids_to_push=objs_to_push,
443
+ )
444
+ log(DEBUG, "Pushed %s objects to servicer.", len(objs_to_push))
445
+ except ValueError as e:
446
+ log(
447
+ ERROR,
448
+ "Pushing objects failed. Potential irrecoverable error: %s",
449
+ str(e),
450
+ )
451
+ log(ERROR, str(e))
342
452
 
343
- log(
344
- INFO,
345
- "[Node] POST /%s: success, created result %s",
346
- PATH_PUSH_MESSAGES,
347
- res.results, # pylint: disable=no-member
348
- )
453
+ # Cleanup
454
+ metadata = None
349
455
 
350
456
  def get_run(run_id: int) -> Run:
351
457
  # Construct the request
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
flwr/client/typing.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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 2025 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Public Flower ClientApp APIs."""
flwr/common/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2020 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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,6 +15,8 @@
15
15
  """Common components shared between server and client."""
16
16
 
17
17
 
18
+ from ..app.error import Error as Error
19
+ from ..app.metadata import Metadata as Metadata
18
20
  from .constant import MessageType as MessageType
19
21
  from .constant import MessageTypeLegacy as MessageTypeLegacy
20
22
  from .context import Context as Context
@@ -23,9 +25,7 @@ from .grpc import GRPC_MAX_MESSAGE_LENGTH
23
25
  from .logger import configure as configure
24
26
  from .logger import log as log
25
27
  from .message import DEFAULT_TTL
26
- from .message import Error as Error
27
28
  from .message import Message as Message
28
- from .message import Metadata as Metadata
29
29
  from .parameter import bytes_to_ndarray as bytes_to_ndarray
30
30
  from .parameter import ndarray_to_bytes as ndarray_to_bytes
31
31
  from .parameter import ndarrays_to_parameters as ndarrays_to_parameters
flwr/common/address.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
flwr/common/args.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
@@ -17,8 +17,10 @@
17
17
 
18
18
  from .auth_plugin import CliAuthPlugin as CliAuthPlugin
19
19
  from .auth_plugin import ExecAuthPlugin as ExecAuthPlugin
20
+ from .auth_plugin import ExecAuthzPlugin as ExecAuthzPlugin
20
21
 
21
22
  __all__ = [
22
23
  "CliAuthPlugin",
23
24
  "ExecAuthPlugin",
25
+ "ExecAuthzPlugin",
24
26
  ]
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
@@ -20,7 +20,7 @@ from collections.abc import Sequence
20
20
  from pathlib import Path
21
21
  from typing import Optional, Union
22
22
 
23
- from flwr.common.typing import UserInfo
23
+ from flwr.common.typing import AccountInfo
24
24
  from flwr.proto.exec_pb2_grpc import ExecStub
25
25
 
26
26
  from ..typing import UserAuthCredentials, UserAuthLoginDetails
@@ -33,6 +33,9 @@ class ExecAuthPlugin(ABC):
33
33
  ----------
34
34
  user_auth_config_path : Path
35
35
  Path to the YAML file containing the authentication configuration.
36
+ verify_tls_cert : bool
37
+ Boolean indicating whether to verify the TLS certificate
38
+ when making requests to the server.
36
39
  """
37
40
 
38
41
  @abstractmethod
@@ -50,7 +53,7 @@ class ExecAuthPlugin(ABC):
50
53
  @abstractmethod
51
54
  def validate_tokens_in_metadata(
52
55
  self, metadata: Sequence[tuple[str, Union[str, bytes]]]
53
- ) -> tuple[bool, Optional[UserInfo]]:
56
+ ) -> tuple[bool, Optional[AccountInfo]]:
54
57
  """Validate authentication tokens in the provided metadata."""
55
58
 
56
59
  @abstractmethod
@@ -60,10 +63,33 @@ class ExecAuthPlugin(ABC):
60
63
  @abstractmethod
61
64
  def refresh_tokens(
62
65
  self, metadata: Sequence[tuple[str, Union[str, bytes]]]
63
- ) -> Optional[Sequence[tuple[str, Union[str, bytes]]]]:
66
+ ) -> tuple[
67
+ Optional[Sequence[tuple[str, Union[str, bytes]]]], Optional[AccountInfo]
68
+ ]:
64
69
  """Refresh authentication tokens in the provided metadata."""
65
70
 
66
71
 
72
+ class ExecAuthzPlugin(ABC): # pylint: disable=too-few-public-methods
73
+ """Abstract Flower Authorization Plugin class for ExecServicer.
74
+
75
+ Parameters
76
+ ----------
77
+ user_auth_config_path : Path
78
+ Path to the YAML file containing the authorization configuration.
79
+ verify_tls_cert : bool
80
+ Boolean indicating whether to verify the TLS certificate
81
+ when making requests to the server.
82
+ """
83
+
84
+ @abstractmethod
85
+ def __init__(self, user_auth_config_path: Path, verify_tls_cert: bool):
86
+ """Abstract constructor."""
87
+
88
+ @abstractmethod
89
+ def verify_user_authorization(self, account_info: AccountInfo) -> bool:
90
+ """Verify user authorization request."""
91
+
92
+
67
93
  class CliAuthPlugin(ABC):
68
94
  """Abstract Flower Auth Plugin class for CLI.
69
95
 
flwr/common/config.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
flwr/common/constant.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
@@ -55,13 +55,14 @@ EXEC_API_DEFAULT_SERVER_ADDRESS = f"{SERVER_OCTET}:{EXEC_API_PORT}"
55
55
  SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS = f"{SERVER_OCTET}:{SIMULATIONIO_PORT}"
56
56
  SIMULATIONIO_API_DEFAULT_CLIENT_ADDRESS = f"{CLIENT_OCTET}:{SIMULATIONIO_PORT}"
57
57
 
58
- # Constants for ping
59
- PING_DEFAULT_INTERVAL = 30
60
- PING_CALL_TIMEOUT = 5
61
- PING_BASE_MULTIPLIER = 0.8
62
- PING_RANDOM_RANGE = (-0.1, 0.1)
63
- PING_MAX_INTERVAL = 1e300
64
- PING_PATIENCE = 2
58
+ # Constants for heartbeat
59
+ HEARTBEAT_DEFAULT_INTERVAL = 30
60
+ HEARTBEAT_CALL_TIMEOUT = 5
61
+ HEARTBEAT_BASE_MULTIPLIER = 0.8
62
+ HEARTBEAT_RANDOM_RANGE = (-0.1, 0.1)
63
+ HEARTBEAT_MAX_INTERVAL = 1e300
64
+ HEARTBEAT_PATIENCE = 2
65
+ RUN_FAILURE_DETAILS_NO_HEARTBEAT = "No heartbeat received from the run."
65
66
 
66
67
  # IDs
67
68
  RUN_ID_NUM_BYTES = 8
@@ -114,6 +115,9 @@ AUTH_TYPE_YAML_KEY = "auth_type" # For key name in YAML file
114
115
  ACCESS_TOKEN_KEY = "flwr-oidc-access-token"
115
116
  REFRESH_TOKEN_KEY = "flwr-oidc-refresh-token"
116
117
 
118
+ # Constants for user authorization
119
+ AUTHZ_TYPE_YAML_KEY = "authz_type" # For key name in YAML file
120
+
117
121
  # Constants for node authentication
118
122
  PUBLIC_KEY_HEADER = "flwr-public-key-bin" # Must end with "-bin" for binary data
119
123
  SIGNATURE_HEADER = "flwr-signature-bin" # Must end with "-bin" for binary data
@@ -121,9 +125,34 @@ TIMESTAMP_HEADER = "flwr-timestamp"
121
125
  TIMESTAMP_TOLERANCE = 10 # General tolerance for timestamp verification
122
126
  SYSTEM_TIME_TOLERANCE = 5 # Allowance for system time drift
123
127
 
128
+ # Constants for grpc retry
129
+ GRPC_RETRY_MAX_DELAY = 20 # Maximum delay duration between two consecutive retries.
130
+
124
131
  # Constants for ArrayRecord
125
132
  GC_THRESHOLD = 200_000_000 # 200 MB
126
133
 
134
+ # Constants for Inflatable
135
+ HEAD_BODY_DIVIDER = b"\x00"
136
+ HEAD_VALUE_DIVIDER = " "
137
+
138
+ # Constants for serialization
139
+ INT64_MAX_VALUE = 9223372036854775807 # (1 << 63) - 1
140
+
141
+ # Constants for `flwr-serverapp` and `flwr-clientapp` CLI commands
142
+ FLWR_APP_TOKEN_LENGTH = 128 # Length of the token used
143
+
144
+ # Constants for object pushing and pulling
145
+ MAX_CONCURRENT_PUSHES = 8 # Default maximum number of concurrent pushes
146
+ MAX_CONCURRENT_PULLS = 8 # Default maximum number of concurrent pulls
147
+ PULL_MAX_TIME = 7200 # Default maximum time to wait for pulling objects
148
+ PULL_MAX_TRIES_PER_OBJECT = 500 # Default maximum number of tries to pull an object
149
+ PULL_INITIAL_BACKOFF = 1 # Initial backoff time for pulling objects
150
+ PULL_BACKOFF_CAP = 10 # Maximum backoff time for pulling objects
151
+
152
+
153
+ # ExecServicer constants
154
+ RUN_ID_NOT_FOUND_MESSAGE = "Run ID not found"
155
+
127
156
 
128
157
  class MessageType:
129
158
  """Message type."""
flwr/common/context.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
flwr/common/date.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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 2024 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
flwr/common/dp.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright 2022 Flower Labs GmbH. All Rights Reserved.
1
+ # Copyright 2025 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.
@@ -21,7 +21,7 @@ from typing import Optional, Union
21
21
  import grpc
22
22
  from google.protobuf.message import Message as GrpcMessage
23
23
 
24
- from flwr.common.typing import LogEntry, UserInfo
24
+ from flwr.common.typing import AccountInfo, LogEntry
25
25
 
26
26
 
27
27
  class EventLogWriterPlugin(ABC):
@@ -36,7 +36,7 @@ class EventLogWriterPlugin(ABC):
36
36
  self,
37
37
  request: GrpcMessage,
38
38
  context: grpc.ServicerContext,
39
- user_info: Optional[UserInfo],
39
+ account_info: Optional[AccountInfo],
40
40
  method_name: str,
41
41
  ) -> LogEntry:
42
42
  """Compose pre-event log entry from the provided request and context."""
@@ -46,7 +46,7 @@ class EventLogWriterPlugin(ABC):
46
46
  self,
47
47
  request: GrpcMessage,
48
48
  context: grpc.ServicerContext,
49
- user_info: Optional[UserInfo],
49
+ account_info: Optional[AccountInfo],
50
50
  method_name: str,
51
51
  response: Optional[Union[GrpcMessage, BaseException]],
52
52
  ) -> LogEntry: