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.
- flwr/__init__.py +1 -1
- flwr/app/__init__.py +15 -0
- flwr/app/error.py +68 -0
- flwr/app/metadata.py +223 -0
- flwr/cli/__init__.py +1 -1
- flwr/cli/app.py +21 -2
- flwr/cli/build.py +83 -58
- flwr/cli/cli_user_auth_interceptor.py +1 -1
- flwr/cli/config_utils.py +53 -17
- flwr/cli/example.py +1 -1
- flwr/cli/install.py +1 -1
- flwr/cli/log.py +4 -4
- flwr/cli/login/__init__.py +1 -1
- flwr/cli/login/login.py +15 -8
- flwr/cli/ls.py +16 -37
- flwr/cli/new/__init__.py +1 -1
- flwr/cli/new/new.py +4 -4
- flwr/cli/new/templates/__init__.py +1 -1
- flwr/cli/new/templates/app/__init__.py +1 -1
- flwr/cli/new/templates/app/code/__init__.py +1 -1
- flwr/cli/new/templates/app/code/client.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/__init__.py +1 -1
- flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +4 -4
- flwr/cli/new/templates/app/code/model.baseline.py.tpl +1 -1
- flwr/cli/new/templates/app/code/server.baseline.py.tpl +2 -3
- flwr/cli/new/templates/app/code/task.sklearn.py.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +14 -17
- flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +4 -4
- flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
- flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
- flwr/cli/run/__init__.py +1 -1
- flwr/cli/run/run.py +11 -19
- flwr/cli/stop.py +3 -3
- flwr/cli/utils.py +42 -17
- flwr/client/__init__.py +3 -3
- flwr/client/client.py +1 -1
- flwr/client/client_app.py +140 -138
- flwr/client/clientapp/__init__.py +1 -8
- flwr/client/clientapp/utils.py +1 -1
- flwr/client/dpfedavg_numpy_client.py +1 -1
- flwr/client/grpc_adapter_client/__init__.py +1 -1
- flwr/client/grpc_adapter_client/connection.py +5 -5
- flwr/client/grpc_rere_client/__init__.py +1 -1
- flwr/client/grpc_rere_client/client_interceptor.py +1 -1
- flwr/client/grpc_rere_client/connection.py +131 -61
- flwr/client/grpc_rere_client/grpc_adapter.py +35 -7
- flwr/client/message_handler/__init__.py +1 -1
- flwr/client/message_handler/message_handler.py +2 -2
- flwr/client/mod/__init__.py +1 -1
- flwr/client/mod/centraldp_mods.py +1 -1
- flwr/client/mod/comms_mods.py +39 -20
- flwr/client/mod/localdp_mod.py +6 -6
- flwr/client/mod/secure_aggregation/__init__.py +1 -1
- flwr/client/mod/secure_aggregation/secagg_mod.py +1 -1
- flwr/client/mod/secure_aggregation/secaggplus_mod.py +1 -1
- flwr/client/mod/utils.py +1 -1
- flwr/client/numpy_client.py +1 -1
- flwr/client/rest_client/__init__.py +1 -1
- flwr/client/rest_client/connection.py +174 -68
- flwr/client/run_info_store.py +1 -1
- flwr/client/typing.py +1 -1
- flwr/clientapp/__init__.py +15 -0
- flwr/common/__init__.py +3 -3
- flwr/common/address.py +1 -1
- flwr/common/args.py +1 -1
- flwr/common/auth_plugin/__init__.py +3 -1
- flwr/common/auth_plugin/auth_plugin.py +30 -4
- flwr/common/config.py +1 -1
- flwr/common/constant.py +37 -8
- flwr/common/context.py +1 -1
- flwr/common/date.py +1 -1
- flwr/common/differential_privacy.py +1 -1
- flwr/common/differential_privacy_constants.py +1 -1
- flwr/common/dp.py +1 -1
- flwr/common/event_log_plugin/event_log_plugin.py +3 -3
- flwr/common/exit/exit.py +6 -6
- flwr/common/exit_handlers.py +31 -1
- flwr/common/grpc.py +1 -1
- flwr/common/heartbeat.py +165 -0
- flwr/common/inflatable.py +290 -0
- flwr/common/inflatable_grpc_utils.py +99 -0
- flwr/common/inflatable_rest_utils.py +99 -0
- flwr/common/inflatable_utils.py +341 -0
- flwr/common/logger.py +1 -1
- flwr/common/message.py +137 -252
- flwr/common/object_ref.py +1 -1
- flwr/common/parameter.py +1 -1
- flwr/common/pyproject.py +1 -1
- flwr/common/record/__init__.py +3 -2
- flwr/common/record/array.py +323 -0
- flwr/common/record/arrayrecord.py +121 -243
- flwr/common/record/configrecord.py +71 -16
- flwr/common/record/conversion_utils.py +2 -2
- flwr/common/record/metricrecord.py +71 -20
- flwr/common/record/recorddict.py +207 -90
- flwr/common/record/typeddict.py +1 -1
- flwr/common/recorddict_compat.py +2 -2
- flwr/common/retry_invoker.py +15 -11
- flwr/common/secure_aggregation/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/__init__.py +1 -1
- flwr/common/secure_aggregation/crypto/shamir.py +52 -30
- flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -1
- flwr/common/secure_aggregation/ndarrays_arithmetic.py +1 -1
- flwr/common/secure_aggregation/quantization.py +1 -1
- flwr/common/secure_aggregation/secaggplus_constants.py +1 -1
- flwr/common/secure_aggregation/secaggplus_utils.py +1 -1
- flwr/common/serde.py +60 -184
- flwr/common/serde_utils.py +175 -0
- flwr/common/telemetry.py +2 -2
- flwr/common/typing.py +6 -4
- flwr/common/version.py +1 -1
- flwr/compat/__init__.py +15 -0
- flwr/compat/client/__init__.py +15 -0
- flwr/{client → compat/client}/app.py +71 -211
- flwr/{client → compat/client}/grpc_client/__init__.py +1 -1
- flwr/{client → compat/client}/grpc_client/connection.py +13 -13
- flwr/compat/common/__init__.py +15 -0
- flwr/compat/server/__init__.py +15 -0
- flwr/compat/server/app.py +174 -0
- flwr/compat/simulation/__init__.py +15 -0
- flwr/proto/__init__.py +1 -1
- flwr/proto/fleet_pb2.py +32 -27
- flwr/proto/fleet_pb2.pyi +49 -35
- flwr/proto/fleet_pb2_grpc.py +117 -13
- flwr/proto/fleet_pb2_grpc.pyi +47 -6
- flwr/proto/heartbeat_pb2.py +33 -0
- flwr/proto/heartbeat_pb2.pyi +66 -0
- flwr/proto/heartbeat_pb2_grpc.py +4 -0
- flwr/proto/heartbeat_pb2_grpc.pyi +4 -0
- flwr/proto/message_pb2.py +28 -11
- flwr/proto/message_pb2.pyi +125 -0
- flwr/proto/recorddict_pb2.py +16 -28
- flwr/proto/recorddict_pb2.pyi +46 -64
- flwr/proto/run_pb2.py +24 -32
- flwr/proto/run_pb2.pyi +4 -52
- flwr/proto/serverappio_pb2.py +32 -23
- flwr/proto/serverappio_pb2.pyi +45 -3
- flwr/proto/serverappio_pb2_grpc.py +138 -34
- flwr/proto/serverappio_pb2_grpc.pyi +54 -13
- flwr/proto/simulationio_pb2.py +12 -11
- flwr/proto/simulationio_pb2_grpc.py +35 -0
- flwr/proto/simulationio_pb2_grpc.pyi +14 -0
- flwr/server/__init__.py +2 -2
- flwr/server/app.py +69 -187
- flwr/server/client_manager.py +1 -1
- flwr/server/client_proxy.py +1 -1
- flwr/server/compat/__init__.py +1 -1
- flwr/server/compat/app.py +1 -1
- flwr/server/compat/app_utils.py +51 -29
- flwr/server/compat/legacy_context.py +1 -1
- flwr/server/criterion.py +1 -1
- flwr/server/fleet_event_log_interceptor.py +2 -2
- flwr/server/grid/grid.py +3 -3
- flwr/server/grid/grpc_grid.py +104 -34
- flwr/server/grid/inmemory_grid.py +5 -4
- flwr/server/history.py +1 -1
- flwr/server/run_serverapp.py +1 -1
- flwr/server/server.py +1 -1
- flwr/server/server_app.py +65 -58
- flwr/server/server_config.py +1 -1
- flwr/server/serverapp/__init__.py +1 -1
- flwr/server/serverapp/app.py +19 -1
- flwr/server/serverapp_components.py +1 -1
- flwr/server/strategy/__init__.py +1 -1
- flwr/server/strategy/aggregate.py +1 -1
- flwr/server/strategy/bulyan.py +2 -2
- flwr/server/strategy/dp_adaptive_clipping.py +17 -17
- flwr/server/strategy/dp_fixed_clipping.py +17 -17
- flwr/server/strategy/dpfedavg_adaptive.py +1 -1
- flwr/server/strategy/dpfedavg_fixed.py +1 -1
- flwr/server/strategy/fault_tolerant_fedavg.py +1 -1
- flwr/server/strategy/fedadagrad.py +1 -1
- flwr/server/strategy/fedadam.py +1 -1
- flwr/server/strategy/fedavg.py +1 -1
- flwr/server/strategy/fedavg_android.py +1 -1
- flwr/server/strategy/fedavgm.py +1 -1
- flwr/server/strategy/fedmedian.py +1 -1
- flwr/server/strategy/fedopt.py +1 -1
- flwr/server/strategy/fedprox.py +1 -1
- flwr/server/strategy/fedtrimmedavg.py +1 -1
- flwr/server/strategy/fedxgb_bagging.py +1 -1
- flwr/server/strategy/fedxgb_cyclic.py +1 -1
- flwr/server/strategy/fedxgb_nn_avg.py +3 -2
- flwr/server/strategy/fedyogi.py +1 -1
- flwr/server/strategy/krum.py +1 -1
- flwr/server/strategy/qfedavg.py +1 -1
- flwr/server/strategy/strategy.py +1 -1
- flwr/server/superlink/__init__.py +1 -1
- flwr/server/superlink/ffs/__init__.py +3 -1
- flwr/server/superlink/ffs/disk_ffs.py +1 -1
- flwr/server/superlink/ffs/ffs.py +1 -1
- flwr/server/superlink/ffs/ffs_factory.py +1 -1
- flwr/server/superlink/fleet/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +14 -4
- flwr/server/superlink/fleet/grpc_bidi/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/flower_service_servicer.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_bridge.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_client_proxy.py +1 -1
- flwr/server/superlink/fleet/grpc_bidi/grpc_server.py +13 -13
- flwr/server/superlink/fleet/grpc_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +102 -8
- flwr/server/superlink/fleet/grpc_rere/server_interceptor.py +1 -1
- flwr/server/superlink/fleet/message_handler/__init__.py +1 -1
- flwr/server/superlink/fleet/message_handler/message_handler.py +136 -19
- flwr/server/superlink/fleet/rest_rere/__init__.py +1 -1
- flwr/server/superlink/fleet/rest_rere/rest_api.py +73 -12
- flwr/server/superlink/fleet/vce/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/__init__.py +1 -1
- flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
- flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
- flwr/server/superlink/fleet/vce/vce_api.py +7 -4
- flwr/server/superlink/linkstate/__init__.py +1 -1
- flwr/server/superlink/linkstate/in_memory_linkstate.py +139 -44
- flwr/server/superlink/linkstate/linkstate.py +54 -21
- flwr/server/superlink/linkstate/linkstate_factory.py +1 -1
- flwr/server/superlink/linkstate/sqlite_linkstate.py +150 -56
- flwr/server/superlink/linkstate/utils.py +34 -30
- flwr/server/superlink/serverappio/serverappio_grpc.py +3 -0
- flwr/server/superlink/serverappio/serverappio_servicer.py +211 -57
- flwr/server/superlink/simulation/__init__.py +1 -1
- flwr/server/superlink/simulation/simulationio_grpc.py +1 -1
- flwr/server/superlink/simulation/simulationio_servicer.py +26 -2
- flwr/server/superlink/utils.py +45 -3
- flwr/server/typing.py +1 -1
- flwr/server/utils/__init__.py +1 -1
- flwr/server/utils/tensorboard.py +1 -1
- flwr/server/utils/validator.py +3 -3
- flwr/server/workflow/__init__.py +1 -1
- flwr/server/workflow/constant.py +1 -1
- flwr/server/workflow/default_workflows.py +1 -1
- flwr/server/workflow/secure_aggregation/__init__.py +1 -1
- flwr/server/workflow/secure_aggregation/secagg_workflow.py +1 -1
- flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +1 -1
- flwr/serverapp/__init__.py +15 -0
- flwr/simulation/__init__.py +1 -1
- flwr/simulation/app.py +18 -1
- flwr/simulation/legacy_app.py +1 -1
- flwr/simulation/ray_transport/__init__.py +1 -1
- flwr/simulation/ray_transport/ray_actor.py +1 -1
- flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
- flwr/simulation/ray_transport/utils.py +1 -1
- flwr/simulation/run_simulation.py +2 -2
- flwr/simulation/simulationio_connection.py +1 -1
- flwr/supercore/__init__.py +15 -0
- flwr/supercore/object_store/__init__.py +24 -0
- flwr/supercore/object_store/in_memory_object_store.py +229 -0
- flwr/supercore/object_store/object_store.py +192 -0
- flwr/supercore/object_store/object_store_factory.py +44 -0
- flwr/superexec/__init__.py +1 -1
- flwr/superexec/app.py +1 -1
- flwr/superexec/deployment.py +7 -3
- flwr/superexec/exec_event_log_interceptor.py +4 -4
- flwr/superexec/exec_grpc.py +8 -4
- flwr/superexec/exec_servicer.py +126 -24
- flwr/superexec/exec_user_auth_interceptor.py +38 -9
- flwr/superexec/executor.py +5 -1
- flwr/superexec/simulation.py +8 -2
- flwr/superlink/__init__.py +15 -0
- flwr/{client/supernode → supernode}/__init__.py +1 -8
- flwr/{client/nodestate/nodestate.py → supernode/cli/__init__.py} +8 -15
- flwr/{client/supernode/app.py → supernode/cli/flower_supernode.py} +4 -13
- flwr/supernode/cli/flwr_clientapp.py +81 -0
- flwr/{client → supernode}/nodestate/__init__.py +1 -1
- flwr/supernode/nodestate/in_memory_nodestate.py +190 -0
- flwr/supernode/nodestate/nodestate.py +212 -0
- flwr/{client → supernode}/nodestate/nodestate_factory.py +1 -1
- flwr/supernode/runtime/__init__.py +15 -0
- flwr/{client/clientapp/app.py → supernode/runtime/run_clientapp.py} +26 -57
- flwr/supernode/servicer/__init__.py +15 -0
- flwr/supernode/servicer/clientappio/__init__.py +24 -0
- flwr/{client/clientapp → supernode/servicer/clientappio}/clientappio_servicer.py +1 -1
- flwr/supernode/start_client_internal.py +491 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/METADATA +6 -5
- flwr-1.19.0.dist-info/RECORD +365 -0
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/WHEEL +1 -1
- {flwr-1.17.0.dist-info → flwr-1.19.0.dist-info}/entry_points.txt +2 -2
- flwr/client/heartbeat.py +0 -74
- flwr/client/nodestate/in_memory_nodestate.py +0 -38
- flwr-1.17.0.dist-info/LICENSE +0 -202
- flwr-1.17.0.dist-info/RECORD +0 -333
flwr/server/grid/grpc_grid.py
CHANGED
|
@@ -28,11 +28,28 @@ from flwr.common.constant import (
|
|
|
28
28
|
SUPERLINK_NODE_ID,
|
|
29
29
|
)
|
|
30
30
|
from flwr.common.grpc import create_channel, on_channel_state_change
|
|
31
|
+
from flwr.common.inflatable import (
|
|
32
|
+
get_all_nested_objects,
|
|
33
|
+
get_object_tree,
|
|
34
|
+
no_object_id_recompute,
|
|
35
|
+
)
|
|
36
|
+
from flwr.common.inflatable_grpc_utils import (
|
|
37
|
+
make_pull_object_fn_grpc,
|
|
38
|
+
make_push_object_fn_grpc,
|
|
39
|
+
)
|
|
40
|
+
from flwr.common.inflatable_utils import (
|
|
41
|
+
inflate_object_from_contents,
|
|
42
|
+
pull_objects,
|
|
43
|
+
push_objects,
|
|
44
|
+
)
|
|
31
45
|
from flwr.common.logger import log, warn_deprecated_feature
|
|
46
|
+
from flwr.common.message import remove_content_from_message
|
|
32
47
|
from flwr.common.retry_invoker import _make_simple_grpc_retry_invoker, _wrap_stub
|
|
33
|
-
from flwr.common.serde import
|
|
48
|
+
from flwr.common.serde import message_to_proto, run_from_proto
|
|
34
49
|
from flwr.common.typing import Run
|
|
35
|
-
from flwr.proto.message_pb2 import
|
|
50
|
+
from flwr.proto.message_pb2 import ( # pylint: disable=E0611
|
|
51
|
+
ConfirmMessageReceivedRequest,
|
|
52
|
+
)
|
|
36
53
|
from flwr.proto.node_pb2 import Node # pylint: disable=E0611
|
|
37
54
|
from flwr.proto.run_pb2 import GetRunRequest, GetRunResponse # pylint: disable=E0611
|
|
38
55
|
from flwr.proto.serverappio_pb2 import ( # pylint: disable=E0611
|
|
@@ -163,7 +180,7 @@ class GrpcGrid(Grid):
|
|
|
163
180
|
def _check_message(self, message: Message) -> None:
|
|
164
181
|
# Check if the message is valid
|
|
165
182
|
if not (
|
|
166
|
-
message.metadata.message_id
|
|
183
|
+
message.metadata.message_id != ""
|
|
167
184
|
and message.metadata.reply_to_message_id == ""
|
|
168
185
|
and message.metadata.ttl > 0
|
|
169
186
|
):
|
|
@@ -198,6 +215,38 @@ class GrpcGrid(Grid):
|
|
|
198
215
|
)
|
|
199
216
|
return [node.node_id for node in res.nodes]
|
|
200
217
|
|
|
218
|
+
def _try_push_message(self, run_id: int, message: Message) -> str:
|
|
219
|
+
"""Push one message and its associated objects."""
|
|
220
|
+
# Compute mapping of message descendants
|
|
221
|
+
all_objects = get_all_nested_objects(message)
|
|
222
|
+
msg_id = message.object_id
|
|
223
|
+
object_tree = get_object_tree(message)
|
|
224
|
+
|
|
225
|
+
# Call GrpcServerAppIoStub method
|
|
226
|
+
res: PushInsMessagesResponse = self._stub.PushMessages(
|
|
227
|
+
PushInsMessagesRequest(
|
|
228
|
+
messages_list=[message_to_proto(remove_content_from_message(message))],
|
|
229
|
+
run_id=run_id,
|
|
230
|
+
message_object_trees=[object_tree],
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# Push objects
|
|
235
|
+
# If Message was added to the LinkState correctly
|
|
236
|
+
if msg_id is not None:
|
|
237
|
+
obj_ids_to_push = set(res.objects_to_push[msg_id].object_ids)
|
|
238
|
+
# Push only object that are not in the store
|
|
239
|
+
push_objects(
|
|
240
|
+
all_objects,
|
|
241
|
+
push_object_fn=make_push_object_fn_grpc(
|
|
242
|
+
push_object_grpc=self._stub.PushObject,
|
|
243
|
+
node=self.node,
|
|
244
|
+
run_id=run_id,
|
|
245
|
+
),
|
|
246
|
+
object_ids_to_push=obj_ids_to_push,
|
|
247
|
+
)
|
|
248
|
+
return msg_id
|
|
249
|
+
|
|
201
250
|
def push_messages(self, messages: Iterable[Message]) -> Iterable[str]:
|
|
202
251
|
"""Push messages to specified node IDs.
|
|
203
252
|
|
|
@@ -206,57 +255,78 @@ class GrpcGrid(Grid):
|
|
|
206
255
|
"""
|
|
207
256
|
# Construct Messages
|
|
208
257
|
run_id = cast(Run, self._run).run_id
|
|
209
|
-
|
|
210
|
-
for msg in messages:
|
|
211
|
-
# Populate metadata
|
|
212
|
-
msg.metadata.__dict__["_run_id"] = run_id
|
|
213
|
-
msg.metadata.__dict__["_src_node_id"] = self.node.node_id
|
|
214
|
-
# Check message
|
|
215
|
-
self._check_message(msg)
|
|
216
|
-
# Convert to proto
|
|
217
|
-
msg_proto = message_to_proto(msg)
|
|
218
|
-
# Add to list
|
|
219
|
-
message_proto_list.append(msg_proto)
|
|
220
|
-
|
|
258
|
+
message_ids: list[str] = []
|
|
221
259
|
try:
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
"passed to `push_messages`). This could be due to a malformed "
|
|
234
|
-
"message.",
|
|
235
|
-
)
|
|
236
|
-
return list(res.message_ids)
|
|
260
|
+
for msg in messages:
|
|
261
|
+
# Populate metadata
|
|
262
|
+
msg.metadata.__dict__["_run_id"] = run_id
|
|
263
|
+
msg.metadata.__dict__["_src_node_id"] = self.node.node_id
|
|
264
|
+
msg.metadata.__dict__["_message_id"] = msg.object_id
|
|
265
|
+
# Check message
|
|
266
|
+
self._check_message(msg)
|
|
267
|
+
# Try pushing message and its objects
|
|
268
|
+
with no_object_id_recompute():
|
|
269
|
+
message_ids.append(self._try_push_message(run_id, msg))
|
|
270
|
+
|
|
237
271
|
except grpc.RpcError as e:
|
|
238
272
|
if e.code() == grpc.StatusCode.RESOURCE_EXHAUSTED: # pylint: disable=E1101
|
|
239
273
|
log(ERROR, ERROR_MESSAGE_PUSH_MESSAGES_RESOURCE_EXHAUSTED)
|
|
240
274
|
return []
|
|
241
275
|
raise
|
|
242
276
|
|
|
277
|
+
if None in message_ids:
|
|
278
|
+
log(
|
|
279
|
+
WARNING,
|
|
280
|
+
"Not all messages could be pushed to the SuperLink. The returned "
|
|
281
|
+
"list has `None` for those messages (the order is preserved as "
|
|
282
|
+
"passed to `push_messages`). This could be due to a malformed "
|
|
283
|
+
"message.",
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
return message_ids
|
|
287
|
+
|
|
243
288
|
def pull_messages(self, message_ids: Iterable[str]) -> Iterable[Message]:
|
|
244
289
|
"""Pull messages based on message IDs.
|
|
245
290
|
|
|
246
291
|
This method is used to collect messages from the SuperLink that correspond to a
|
|
247
292
|
set of given message IDs.
|
|
248
293
|
"""
|
|
294
|
+
run_id = cast(Run, self._run).run_id
|
|
249
295
|
try:
|
|
250
296
|
# Pull Messages
|
|
251
297
|
res: PullResMessagesResponse = self._stub.PullMessages(
|
|
252
298
|
PullResMessagesRequest(
|
|
253
299
|
message_ids=message_ids,
|
|
254
|
-
run_id=
|
|
300
|
+
run_id=run_id,
|
|
255
301
|
)
|
|
256
302
|
)
|
|
257
|
-
#
|
|
258
|
-
|
|
259
|
-
|
|
303
|
+
# Pull Messages from store
|
|
304
|
+
inflated_msgs: list[Message] = []
|
|
305
|
+
for msg_proto in res.messages_list:
|
|
306
|
+
msg_id = msg_proto.metadata.message_id
|
|
307
|
+
all_object_contents = pull_objects(
|
|
308
|
+
list(res.objects_to_pull[msg_id].object_ids) + [msg_id],
|
|
309
|
+
pull_object_fn=make_pull_object_fn_grpc(
|
|
310
|
+
pull_object_grpc=self._stub.PullObject,
|
|
311
|
+
node=self.node,
|
|
312
|
+
run_id=run_id,
|
|
313
|
+
),
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Confirm that the message has been received
|
|
317
|
+
self._stub.ConfirmMessageReceived(
|
|
318
|
+
ConfirmMessageReceivedRequest(
|
|
319
|
+
node=self.node, run_id=run_id, message_object_id=msg_id
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
message = cast(
|
|
323
|
+
Message, inflate_object_from_contents(msg_id, all_object_contents)
|
|
324
|
+
)
|
|
325
|
+
message.metadata.__dict__["_message_id"] = msg_id
|
|
326
|
+
inflated_msgs.append(message)
|
|
327
|
+
|
|
328
|
+
return inflated_msgs
|
|
329
|
+
|
|
260
330
|
except grpc.RpcError as e:
|
|
261
331
|
if e.code() == grpc.StatusCode.RESOURCE_EXHAUSTED: # pylint: disable=E1101
|
|
262
332
|
log(ERROR, ERROR_MESSAGE_PULL_MESSAGES_RESOURCE_EXHAUSTED)
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import time
|
|
19
19
|
from collections.abc import Iterable
|
|
20
20
|
from typing import Optional, cast
|
|
21
|
-
from uuid import
|
|
21
|
+
from uuid import uuid4
|
|
22
22
|
|
|
23
23
|
from flwr.common import Message, RecordDict
|
|
24
24
|
from flwr.common.constant import SUPERLINK_NODE_ID
|
|
@@ -56,7 +56,7 @@ class InMemoryGrid(Grid):
|
|
|
56
56
|
def _check_message(self, message: Message) -> None:
|
|
57
57
|
# Check if the message is valid
|
|
58
58
|
if not (
|
|
59
|
-
message.metadata.message_id
|
|
59
|
+
message.metadata.message_id != ""
|
|
60
60
|
and message.metadata.reply_to_message_id == ""
|
|
61
61
|
and message.metadata.ttl > 0
|
|
62
62
|
and message.metadata.delivered_at == ""
|
|
@@ -111,6 +111,7 @@ class InMemoryGrid(Grid):
|
|
|
111
111
|
# Populate metadata
|
|
112
112
|
msg.metadata.__dict__["_run_id"] = cast(Run, self._run).run_id
|
|
113
113
|
msg.metadata.__dict__["_src_node_id"] = self.node.node_id
|
|
114
|
+
msg.metadata.__dict__["_message_id"] = str(uuid4())
|
|
114
115
|
# Check message
|
|
115
116
|
self._check_message(msg)
|
|
116
117
|
# Store in state
|
|
@@ -126,12 +127,12 @@ class InMemoryGrid(Grid):
|
|
|
126
127
|
This method is used to collect messages from the SuperLink that correspond to a
|
|
127
128
|
set of given message IDs.
|
|
128
129
|
"""
|
|
129
|
-
msg_ids =
|
|
130
|
+
msg_ids = set(message_ids)
|
|
130
131
|
# Pull Messages
|
|
131
132
|
message_res_list = self.state.get_message_res(message_ids=msg_ids)
|
|
132
133
|
# Get IDs of Messages these replies are for
|
|
133
134
|
message_ins_ids_to_delete = {
|
|
134
|
-
|
|
135
|
+
msg_res.metadata.reply_to_message_id for msg_res in message_res_list
|
|
135
136
|
}
|
|
136
137
|
# Delete
|
|
137
138
|
self.state.delete_messages(message_ins_ids=message_ins_ids_to_delete)
|
flwr/server/history.py
CHANGED
flwr/server/run_serverapp.py
CHANGED
flwr/server/server.py
CHANGED
flwr/server/server_app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
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.
|
|
@@ -52,6 +52,30 @@ GRID_USAGE_EXAMPLE = """
|
|
|
52
52
|
# Your existing ServerApp code ...
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
|
+
BOTH_MAIN_FN_SERVER_FN_PROVIDED_ERROR_MSG = (
|
|
56
|
+
"Use either a custom main function or a `Strategy`, but not both."
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
Use the `ServerApp` with an existing `Strategy`:
|
|
60
|
+
|
|
61
|
+
server_config = ServerConfig(num_rounds=3)
|
|
62
|
+
strategy = FedAvg()
|
|
63
|
+
|
|
64
|
+
app = ServerApp(
|
|
65
|
+
server_config=server_config,
|
|
66
|
+
strategy=strategy,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
Use the `ServerApp` with a custom main function:
|
|
70
|
+
|
|
71
|
+
app = ServerApp()
|
|
72
|
+
|
|
73
|
+
@app.main()
|
|
74
|
+
def main(grid: Grid, context: Context) -> None:
|
|
75
|
+
print("ServerApp running")
|
|
76
|
+
"""
|
|
77
|
+
)
|
|
78
|
+
|
|
55
79
|
DRIVER_DEPRECATION_MSG = """
|
|
56
80
|
The `Driver` class is deprecated, it will be removed in a future release.
|
|
57
81
|
"""
|
|
@@ -70,25 +94,25 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
|
70
94
|
|
|
71
95
|
Examples
|
|
72
96
|
--------
|
|
73
|
-
Use the ``ServerApp`` with an existing ``Strategy
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
Use the ``ServerApp`` with a custom main function
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
97
|
+
Use the ``ServerApp`` with an existing ``Strategy``::
|
|
98
|
+
|
|
99
|
+
def server_fn(context: Context):
|
|
100
|
+
server_config = ServerConfig(num_rounds=3)
|
|
101
|
+
strategy = FedAvg()
|
|
102
|
+
return ServerAppComponents(
|
|
103
|
+
strategy=strategy,
|
|
104
|
+
server_config=server_config,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
app = ServerApp(server_fn=server_fn)
|
|
108
|
+
|
|
109
|
+
Use the ``ServerApp`` with a custom main function::
|
|
110
|
+
|
|
111
|
+
app = ServerApp()
|
|
112
|
+
|
|
113
|
+
@app.main()
|
|
114
|
+
def main(grid: Grid, context: Context) -> None:
|
|
115
|
+
print("ServerApp running")
|
|
92
116
|
"""
|
|
93
117
|
|
|
94
118
|
# pylint: disable=too-many-arguments,too-many-positional-arguments
|
|
@@ -156,38 +180,19 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
|
156
180
|
|
|
157
181
|
Examples
|
|
158
182
|
--------
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
183
|
+
::
|
|
184
|
+
|
|
185
|
+
app = ServerApp()
|
|
186
|
+
|
|
187
|
+
@app.main()
|
|
188
|
+
def main(grid: Grid, context: Context) -> None:
|
|
189
|
+
print("ServerApp running")
|
|
164
190
|
"""
|
|
165
191
|
|
|
166
192
|
def main_decorator(main_fn: ServerAppCallable) -> ServerAppCallable:
|
|
167
193
|
"""Register the main fn with the ServerApp object."""
|
|
168
194
|
if self._server or self._config or self._strategy or self._client_manager:
|
|
169
|
-
raise ValueError(
|
|
170
|
-
"""Use either a custom main function or a `Strategy`, but not both.
|
|
171
|
-
|
|
172
|
-
Use the `ServerApp` with an existing `Strategy`:
|
|
173
|
-
|
|
174
|
-
>>> server_config = ServerConfig(num_rounds=3)
|
|
175
|
-
>>> strategy = FedAvg()
|
|
176
|
-
>>>
|
|
177
|
-
>>> app = ServerApp(
|
|
178
|
-
>>> server_config=server_config,
|
|
179
|
-
>>> strategy=strategy,
|
|
180
|
-
>>> )
|
|
181
|
-
|
|
182
|
-
Use the `ServerApp` with a custom main function:
|
|
183
|
-
|
|
184
|
-
>>> app = ServerApp()
|
|
185
|
-
>>>
|
|
186
|
-
>>> @app.main()
|
|
187
|
-
>>> def main(grid: Grid, context: Context) -> None:
|
|
188
|
-
>>> print("ServerApp running")
|
|
189
|
-
""",
|
|
190
|
-
)
|
|
195
|
+
raise ValueError(BOTH_MAIN_FN_SERVER_FN_PROVIDED_ERROR_MSG)
|
|
191
196
|
|
|
192
197
|
sig = inspect.signature(main_fn)
|
|
193
198
|
param = list(sig.parameters.values())[0]
|
|
@@ -219,17 +224,19 @@ class ServerApp: # pylint: disable=too-many-instance-attributes
|
|
|
219
224
|
|
|
220
225
|
Examples
|
|
221
226
|
--------
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
227
|
+
::
|
|
228
|
+
|
|
229
|
+
app = ServerApp()
|
|
230
|
+
|
|
231
|
+
@app.lifespan()
|
|
232
|
+
def lifespan(context: Context) -> None:
|
|
233
|
+
# Perform initialization tasks before the app starts
|
|
234
|
+
print("Initializing ServerApp")
|
|
235
|
+
|
|
236
|
+
yield # ServerApp is running
|
|
237
|
+
|
|
238
|
+
# Perform cleanup tasks after the app stops
|
|
239
|
+
print("Cleaning up ServerApp")
|
|
233
240
|
"""
|
|
234
241
|
|
|
235
242
|
def lifespan_decorator(
|
flwr/server/server_config.py
CHANGED
flwr/server/serverapp/app.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
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.
|
|
@@ -38,6 +38,7 @@ from flwr.common.constant import (
|
|
|
38
38
|
SubStatus,
|
|
39
39
|
)
|
|
40
40
|
from flwr.common.exit import ExitCode, flwr_exit
|
|
41
|
+
from flwr.common.heartbeat import HeartbeatSender, get_grpc_app_heartbeat_fn
|
|
41
42
|
from flwr.common.logger import (
|
|
42
43
|
log,
|
|
43
44
|
mirror_output_to_queue,
|
|
@@ -117,6 +118,7 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
|
|
|
117
118
|
success = True
|
|
118
119
|
hash_run_id = None
|
|
119
120
|
run_status = None
|
|
121
|
+
heartbeat_sender = None
|
|
120
122
|
while True:
|
|
121
123
|
|
|
122
124
|
try:
|
|
@@ -182,6 +184,16 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
|
|
|
182
184
|
event_details={"run-id-hash": hash_run_id},
|
|
183
185
|
)
|
|
184
186
|
|
|
187
|
+
# Set up heartbeat sender
|
|
188
|
+
heartbeat_fn = get_grpc_app_heartbeat_fn(
|
|
189
|
+
grid._stub,
|
|
190
|
+
run.run_id,
|
|
191
|
+
failure_message="Heartbeat failed unexpectedly. The SuperLink could "
|
|
192
|
+
"not find the provided run ID, or the run status is invalid.",
|
|
193
|
+
)
|
|
194
|
+
heartbeat_sender = HeartbeatSender(heartbeat_fn)
|
|
195
|
+
heartbeat_sender.start()
|
|
196
|
+
|
|
185
197
|
# Load and run the ServerApp with the Grid
|
|
186
198
|
updated_context = run_(
|
|
187
199
|
grid=grid,
|
|
@@ -213,6 +225,11 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
|
|
|
213
225
|
success = False
|
|
214
226
|
|
|
215
227
|
finally:
|
|
228
|
+
# Stop heartbeat sender
|
|
229
|
+
if heartbeat_sender:
|
|
230
|
+
heartbeat_sender.stop()
|
|
231
|
+
heartbeat_sender = None
|
|
232
|
+
|
|
216
233
|
# Stop log uploader for this run and upload final logs
|
|
217
234
|
if log_uploader:
|
|
218
235
|
stop_log_uploader(log_queue, log_uploader)
|
|
@@ -226,6 +243,7 @@ def run_serverapp( # pylint: disable=R0914, disable=W0212, disable=R0915
|
|
|
226
243
|
run_id=run.run_id, run_status=run_status_proto
|
|
227
244
|
)
|
|
228
245
|
)
|
|
246
|
+
|
|
229
247
|
event(
|
|
230
248
|
EventType.FLWR_SERVERAPP_RUN_LEAVE,
|
|
231
249
|
event_details={"run-id-hash": hash_run_id, "success": success},
|
flwr/server/strategy/__init__.py
CHANGED
flwr/server/strategy/bulyan.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
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.
|
|
@@ -37,7 +37,7 @@ from .aggregate import aggregate_bulyan, aggregate_krum
|
|
|
37
37
|
from .fedavg import FedAvg
|
|
38
38
|
|
|
39
39
|
|
|
40
|
-
#
|
|
40
|
+
# noqa: E501
|
|
41
41
|
# pylint: disable=line-too-long
|
|
42
42
|
class Bulyan(FedAvg):
|
|
43
43
|
"""Bulyan strategy.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
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.
|
|
@@ -77,15 +77,15 @@ class DifferentialPrivacyServerSideAdaptiveClipping(Strategy):
|
|
|
77
77
|
|
|
78
78
|
Examples
|
|
79
79
|
--------
|
|
80
|
-
Create a strategy
|
|
80
|
+
Create a strategy::
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
strategy = fl.server.strategy.FedAvg( ... )
|
|
83
83
|
|
|
84
|
-
Wrap the strategy with the DifferentialPrivacyServerSideAdaptiveClipping wrapper
|
|
84
|
+
Wrap the strategy with the DifferentialPrivacyServerSideAdaptiveClipping wrapper::
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
dp_strategy = DifferentialPrivacyServerSideAdaptiveClipping(
|
|
87
|
+
strategy, cfg.noise_multiplier, cfg.num_sampled_clients, ...
|
|
88
|
+
)
|
|
89
89
|
"""
|
|
90
90
|
|
|
91
91
|
# pylint: disable=too-many-arguments,too-many-instance-attributes,too-many-positional-arguments
|
|
@@ -290,21 +290,21 @@ class DifferentialPrivacyClientSideAdaptiveClipping(Strategy):
|
|
|
290
290
|
|
|
291
291
|
Examples
|
|
292
292
|
--------
|
|
293
|
-
Create a strategy
|
|
293
|
+
Create a strategy::
|
|
294
294
|
|
|
295
|
-
|
|
295
|
+
strategy = fl.server.strategy.FedAvg(...)
|
|
296
296
|
|
|
297
|
-
Wrap the strategy with the `DifferentialPrivacyClientSideAdaptiveClipping` wrapper
|
|
297
|
+
Wrap the strategy with the `DifferentialPrivacyClientSideAdaptiveClipping` wrapper::
|
|
298
298
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
dp_strategy = DifferentialPrivacyClientSideAdaptiveClipping(
|
|
300
|
+
strategy, cfg.noise_multiplier, cfg.num_sampled_clients
|
|
301
|
+
)
|
|
302
302
|
|
|
303
|
-
On the client, add the `adaptiveclipping_mod` to the client-side mods
|
|
303
|
+
On the client, add the `adaptiveclipping_mod` to the client-side mods::
|
|
304
304
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
305
|
+
app = fl.client.ClientApp(
|
|
306
|
+
client_fn=client_fn, mods=[adaptiveclipping_mod]
|
|
307
|
+
)
|
|
308
308
|
"""
|
|
309
309
|
|
|
310
310
|
# pylint: disable=too-many-arguments,too-many-instance-attributes,too-many-positional-arguments
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright
|
|
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.
|
|
@@ -64,15 +64,15 @@ class DifferentialPrivacyServerSideFixedClipping(Strategy):
|
|
|
64
64
|
|
|
65
65
|
Examples
|
|
66
66
|
--------
|
|
67
|
-
Create a strategy
|
|
67
|
+
Create a strategy::
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
strategy = fl.server.strategy.FedAvg( ... )
|
|
70
70
|
|
|
71
|
-
Wrap the strategy with the DifferentialPrivacyServerSideFixedClipping wrapper
|
|
71
|
+
Wrap the strategy with the DifferentialPrivacyServerSideFixedClipping wrapper::
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
dp_strategy = DifferentialPrivacyServerSideFixedClipping(
|
|
74
|
+
strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
|
|
75
|
+
)
|
|
76
76
|
"""
|
|
77
77
|
|
|
78
78
|
# pylint: disable=too-many-arguments,too-many-instance-attributes
|
|
@@ -228,21 +228,21 @@ class DifferentialPrivacyClientSideFixedClipping(Strategy):
|
|
|
228
228
|
|
|
229
229
|
Examples
|
|
230
230
|
--------
|
|
231
|
-
Create a strategy
|
|
231
|
+
Create a strategy::
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
strategy = fl.server.strategy.FedAvg(...)
|
|
234
234
|
|
|
235
|
-
Wrap the strategy with the `DifferentialPrivacyClientSideFixedClipping` wrapper
|
|
235
|
+
Wrap the strategy with the `DifferentialPrivacyClientSideFixedClipping` wrapper::
|
|
236
236
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
237
|
+
dp_strategy = DifferentialPrivacyClientSideFixedClipping(
|
|
238
|
+
strategy, cfg.noise_multiplier, cfg.clipping_norm, cfg.num_sampled_clients
|
|
239
|
+
)
|
|
240
240
|
|
|
241
|
-
On the client, add the `fixedclipping_mod` to the client-side mods
|
|
241
|
+
On the client, add the `fixedclipping_mod` to the client-side mods::
|
|
242
242
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
app = fl.client.ClientApp(
|
|
244
|
+
client_fn=client_fn, mods=[fixedclipping_mod]
|
|
245
|
+
)
|
|
246
246
|
"""
|
|
247
247
|
|
|
248
248
|
# pylint: disable=too-many-arguments,too-many-instance-attributes
|